Awaitable Console Application

The other days someone posted some code about how to make Async/Await easier to work with. What they went for was not correct, and looked more like RXs model of OnNext/OnError/OnCompleted. The reason it was wrong is that it did not support awaiting.

As a rule any Asyn code you write WILL need synchronizing at some point, so must be awaitable. This is the reason why async void is a very very dodgy thing and should only be used for event handlers, but lets not even get into that.

Now when I was talking to the chap who wrote the original stuff I pointed out more typical use case would have been to do something like this

class Program
{
    static void Main(string[] args)
    {

        new Program().Backup();
        Console.ReadLine();
    }


    public async void Backup()
    {
        var backupStatus = await BackupAsync();
    }


    public async Task<string> BackupAsync()
    {
        return await Task.Run(() => "groovy");
    }

}

Another eagle eyed reader pointed out that you should never use async void (I was doing that to be as much in line with the original posters code), and that the only reason my code worked was due to the use of the Console.ReadLine() which was blocking the main thread.

I did know that I should never really use async void, so I set about trying to post a better version of this. One important note here is that I am using a ConsoleApplication. So I tried this

class Program
{
    private  static async void Main(string[] args)
    {
        await new Program().Backup();
    }


    public async Task Backup()
    {
        var backupStatus = await BackupAsync();
        await Task.Delay(5000); //simulate some work
        Console.WriteLine(backupStatus);
    }


    public async Task<string> BackupAsync()
    {
        return await Task.Run(() => "groovy");
    }
}

The compiler complained about this, and would not allow a async void main method. This is the error

‘NonAwaitingConsoleApplication.Program.Main(string[])’: an entry point cannot be marked with the ‘async’ modifier

Ok so how about this one then

class Program
{
    private static void Main(string[] args)
    {
        Task.Run(() => MainAsync(args)).Wait();
    }

    static async void MainAsync(string[] args)
    {
        Console.WriteLine(DateTime.Now.ToLongTimeString());
        await new Program().Backup();
        Console.WriteLine(DateTime.Now.ToLongTimeString());
    }

    public async Task Backup()
    {
        var backupStatus = await BackupAsync();
        await Task.Delay(5000); //simulate some work
        Console.WriteLine(backupStatus);
    }


    public async Task<string> BackupAsync()
    {
        return await Task.Run(() => "groovy");
    }
}

And that exited straight away…mmmmm. Interesting. The reason for this, is that there is no SynchronizationContext in a ConsoleApplication. So the thread is simply returned to the OS, and the app exits prematurely. Not quite what we are after. What can we do?

Well luckily there is a very clever chap (who I highly rate) called Stephen Cleary, who has written a nice set of Extensions which is also written by Stephen Toub (he knows his onions for sure), so I have full trust in this library. It is called NitoAsyncEx. It is also available via NuGet : Nito.AsyncEx

Anyway with this in place we can now create an Awaiting ConsoleApplication like this

internal class Program
{
    private static void Main(string[] args)
    {
        AsyncContext.Run(() => MainAsync(args));
    }

    static async void MainAsync(string[] args)
    {
        Console.WriteLine(DateTime.Now.ToLongTimeString());
        await new Program().Backup();
        Console.WriteLine(DateTime.Now.ToLongTimeString());
    }

    public async Task Backup()
    {
        var backupStatus = await BackupAsync();
        await Task.Delay(5000); //simulate some work
        Console.WriteLine(backupStatus);
    }


    public async Task<string> BackupAsync()
    {
        return await Task.Run(() => "groovy");
    }
}

Notice the use of the AsyncContext that is the NitoAsyncEx magic class that makes it all possible. I urge you to have a look inside that class (you can do so using Reflector or via the Codeplex site source tab), and see how it works. Stephen is doing quite a bit on your behalf, like ensuring there is a valid SynchronizationContext. Please have a look at it. In fact look at it all its a very useful library

Which when run produces the following output, as expected, then exits (as expected)

  • 10:52:21
  • groovy
  • 10:52:26
Advertisements

One thought on “Awaitable Console Application

  1. I think the reason the code in block #3 returned immediately is not because of the lack of a synchronization context, but because you passed an async void to Task.Run. Task.Run has no idea what to wait on in that case.

    If you change MainAsync to return Task instead of void, then Main can simply call MainAsync(args).Wait() and it should work as expected without the need for AsyncContext.

    There’s no reason to use a synchronization context in a console app unless for some reason you *need* continuations to run on the same thread, which in most cases is going to hurt concurrency anyhow.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: