Click here to Skip to main content
Click here to Skip to main content

Tagged as

Awaitable Console Application

, 1 May 2014
Rate this:
Please Sign up or sign in to vote.
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 synchr

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
SuggestionEven shorter and without 3rd party libraries... PinmemberLeonneke2-May-14 3:29 
GeneralRe: Even shorter and without 3rd party libraries... PinmvpSacha Barber2-May-14 4:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 1 May 2014
Article Copyright 2014 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid