Console App Terminating Before async Call Completion
Asked Answered
D

4

40

I'm currently writing a C# console app that generates a number of URLs that point to different images on a web site and then downloads as byte streams using WebClient.DownloadDataAsync().

My issue is that once the first asynchronous call is made, the console app considers the program to be completed and terminates before the asynchronous call can return. By using a Console.Read() I can force the console to stay open but this doesn't seem like very good design. Furthermore if the user hits enter during the process (while the console is waiting for input) the program will terminate.

Is there a better way to prevent the console from closing while I am waiting for an asynchronous call to return?

Edit: the calls are asynchronous because I am providing a status indicator via the console to the user while the downloads take place.

Desrochers answered 1/10, 2010 at 15:45 Comment(0)
O
52

Yes. Use a ManualResetEvent, and have the async callback call event.Set(). If the Main routine blocks on event.WaitOne(), it won't exit until the async code completes.

The basic pseudo-code would look like:

static ManualResetEvent resetEvent = new ManualResetEvent(false);

static void Main()
{
     CallAsyncMethod();
     resetEvent.WaitOne(); // Blocks until "set"
}

void DownloadDataCallback()
{
     // Do your processing on completion...

     resetEvent.Set(); // Allow the program to exit
}
Ornamentation answered 1/10, 2010 at 15:48 Comment(3)
Thanks! This makes sense and works perfectly... much better than console.read!Desrochers
I also find this useful in debugging Windows Services using a long-running Task as the primary worker. In Program.Main(), using a pre-processor directive like #if DEBUG [debug hook] #else [normal service code] #endif And then in the "debug hook" I just add a call to resetEvent.WaitOne() after new MyService().OnStart(). Then you can use VS debug to continuously run in debug mode without modifying your actual service code in any meaningful way.Dasheen
In the OP's specific case it might work, but in general this causes deadlocks if a background task schedules something on the main thread and waits for it to complete. Just a small warning.Fluvial
A
29

Another option is to just call an async Main method and wait on the task returned.

static void Main(string[] args)
{
    MainAsync(args).Wait();
}

static async Task MainAsync(string[] args)
{
    // .. code goes here
}

Since .NET 4.5 you can now call GetAwaiter().GetResult()

static void Main(string[] args)
{
    MainAsync(args).GetAwaiter().GetResult();
}

What is the difference between .Wait() vs .GetAwaiter().GetResult()?

Arondell answered 29/1, 2013 at 3:10 Comment(1)
Nice and simple, exactly what I was looking for.Shortterm
M
6

If that is all your application is doing, I would suggest leaving out the async call; you specifically want the app to 'hang' until the download is complete, so using an async operation is contrary to what you want here, especially in a console app.

Metage answered 1/10, 2010 at 15:49 Comment(4)
I left this out in my problem explanation but during the download I display a progress indicator to the console window. We are talking thousands of images so it will be valuable to the end user to know what is going on.Desrochers
That's fine; you can still update the console from the same thread, too; the user just won't be able to do anything but Control-C out, but that's the behavior you are looking for, anyway.Metage
Using async can also be beneficial if you ever need to extend this to download multiple files, etc... wasn't sure of the rationale here, but if it's just one file, synchronous would be easier..Ornamentation
@Reed I should have been clearer that I was assuming he's only doing a single file, yes. Thanks, Reed! +1Metage
S
1

It should be stated that in C# 7.1 Main can now be marked as async.

static async Task Main()
{
  await LongRunningOperation();
}
Smallclothes answered 11/6, 2018 at 15:8 Comment(1)
This call don't avoid the console quitSnowwhite

© 2022 - 2024 — McMap. All rights reserved.