17

I'm calling an async method within my console application. I don't want the app to quit shortly after it starts, i.e. before the awaitable tasks complete. It seems like I can do this:

internal static void Main(string[] args)
{
    try
    {
        Task.WaitAll(DoThisAsync());
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
        throw;
    }
}

internal static async Task DoThisAsync()
{
    //...
}

But according to Stephen Cleary's article it seems like I can't do that and should instead create some kind of context for the async to return to when it's done (e.g. AsyncContext).

The code above works though, and it returns on the main thread after Task.WaitAll(DoThisAsync());, so why do I need to use a custom context?

rory.ap
  • 34,009
  • 10
  • 83
  • 174
  • Maybe this happens when you use threadpool. Threadpool threads are background threads and only main thread is foreground. UI applications will have a main loop that doesn't allow them to exit before somethings happens, and console applications usually do not have this loop. So, if you don't block your main thread waiting for something, your application will simply exit and the work you were doing on a threadpool will be gone. I'm not sure though. – JustSomeGuy Mar 03 '15 at 19:37
  • As mentioned by @StephenCleary, it's just a preference. It doesn't really matter how you block the thread, you just need to be aware that if you don't somehow manage that thread, your application will exit (even if your other threads aren't done) – Brandon Mar 03 '15 at 19:51
  • @Brandon Well, it *matters*. They function differently, but it's of course possible to write working programs using either approach. – Servy Mar 03 '15 at 20:01

1 Answers1

30

It's not required; it's just my preference.

You can synchronously block on a task within Main (using Wait/Result/WaitAll). The semantics are slightly different; in particular, if the async code fails, then Wait/Result/WaitAll will wrap the exception in an AggregateException, while AsyncContext does not.

Also, AsyncContext treats the main thread specially; instead of sending continuations to the thread pool, it will send them back to that main thread (by default; you can always use ConfigureAwait(false) to avoid this). I find this useful if I'm writing a "proof of concept" console app, because AsyncContext behaves very similarly to the UI contexts.

But at the end of the day, it's just a matter of preference.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I'll have to remember a key purpose of having AsyncContext is to simulate a UI. – Chris Marisic Jun 08 '15 at 19:29
  • 1
    Why does [AsyncContext](https://github.com/StephenCleary/AsyncEx/tree/master/Source/Nito.AsyncEx%20(NET45%2C%20Win8%2C%20WP8%2C%20WPA81)) require so much code if all you're doing is preventing `AggregateException`? I see a TaskScheduler, TaskQueue (BlockingQueue), TaskFactory, and AsyncContextSynchronizationContext. I see an AsyncContextThread which says it performs the actions for the AsyncContext. I guess my point is that there seems to be a lot going on here, but I haven't found a good write-up on what is actually happening. I don't have time to try and decipher what's happening. – crush Oct 28 '15 at 23:34
  • Do you have a blog write-up somewhere that explains what is happening under the hood? All I ever find is that `.Result` and `.Wait()` are bad, and I should use your `AsyncContext` instead. Why? What does `AsyncContext` do differently that makes it a better choice? It seems to me you are scheduling the tasks to run on a background thread, so that if the main thread exits, they will continue to run to completion? Is that correct? – crush Oct 28 '15 at 23:40
  • Seems I found what I was looking for in [this answer](http://stackoverflow.com/a/8767406/1195273) of yours. Basically, your `AsyncContext` is doing the same thing that the `async` keyword is doing behind the scenes, if I'm understanding correctly. – crush Oct 28 '15 at 23:57
  • 2
    @crush: No, the `AsyncContext` basically acts as a message loop that only exits when all asynchronous operations have been completed. I don't recommend using it as a `Result` replacement in the general case. In the case of a console's `Main` method, using either `Result` or `AsyncContext` is just fine; in the general case, you should use `await` instead of `Result`, and definitely not `AsyncContext`. – Stephen Cleary Oct 29 '15 at 00:31
  • 1
    @StephenCleary Thanks for replying. I just found (reading) your [2013 article on Async/Await best practices](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx), and it really clears things up. This sums my situation up fairly well: "This is an especially common problem for programmers who are “dipping their toes” into asynchronous programming, ***converting just a small part of their application and wrapping it in a synchronous API so the rest of the application is isolated from the changes.***" Seems that `AsyncContext` might exist to help programmers with the transition? – crush Oct 29 '15 at 00:53
  • 1
    @crush: It's one of the possibilities, but I still don't recommend it because of reentrancy concerns. I have a more recent [article on `async` brownfield development](https://msdn.microsoft.com/en-us/magazine/mt238404.aspx) that you may find useful. – Stephen Cleary Oct 29 '15 at 01:04