async/await in C# – a disaster waiting to happen?

Ok so that might be a bit overdramatic but now that I have your attention, please continue… :)

Here’s the thing

I love the asynchronous programming model that comes with .NET 4.5 and the async/await keywords in C#5 in since it alleviates the pain of…well…asynchronous programming by allowing for an imperative control flow in the code instead of a inversion-of-control-callback-galore style of code. However, the linear style that it promotes can be deceptive if you don’t remind your self from time to time of how it operates under the hood. I recently ran into the following issue…

Capturing a photo in a Windows Store app

At our company we’re in the process of building Windows Store applications which means we’re using the WinRT API. WinRT is an asynchronous API, most methods are asynchronous and don’t have any synchronous versions. This is a good thing, Microsoft wants us to build responsive apps (lets hope they practice what they preach on this matter… yes I’m looking at you Visual Studio team ;)).

One of the things our app has to provide is the ability to take pictures and the handler for doing this looks something like so:

Which does: tell WinRT to capture image, store image stream to file, save meta data associated with image (guid, path to image, user provided data) in data structure, store meta data to file. The code has two asynchronous calls, one for capturing the image and one for storing the image file but since we’re using the await keyword the code has a nice linear style instead of nested callbacks.

Now, lets do an innocent refactor for this code so that the concerns of storing and saving files and meta data for the image is done by another object:

Now AcquireFromCamera is much shorter and we’ve separated out the logic for handling the image captured event. We’ve also created a buggy piece of software. Take a look at the factored out ImageCaptured handler. The asynchronous method for creating the file, CreateFileAsync, is awaited and as such it will immediately return control to the calling method namely AcquireFromCamera. This behavior hasn’t changed but was has changed is the caller and the continuation i.e. the code that will be executed after the awaited method in the current method scope. In the refactored code, the last line in AcquireFromCamera - Frame.Navigate - will quite possibly be executed before the continuation of CreateFileAsync. This could not happen in the original code since the call to Frame.Navigate was a part of the continuation of CreateFileAsync. In the refactored version the code invoked by Frame.Navigate was in fact executed before the continuation of CreateFileAsync. Since the latter code mutates a state consumed by the former, needless to say, disaster occurred.

Note that the same mistake would have been less likely when using callbacks as continuations since these are explicitly scoped and the refactoring would require moving out code from the callback method to the outer scope.

Be careful out there

While the async and await keywords lets you write code that has a seemingly linear flow, after the compiler has done its magic it’s nothing of the sort. What you should always remember is this:

  • An asynchronous method call will either produce a result immediately if one is available and continue execution in the current method or it will produce a result at a later time and immediately return control to the caller of the current method.
  • The code that comes after an awaited method – the continuation – is what will be executed when the awaited method returns with a result.

Bottom line, be careful of how you structure and restructure your code when using async/await.

27 Comments

  1. Interesting article, the refactored code shouldn’t be compilable in the sample above since you cannot await an “async void” method.

    The simplist way to think about “async void” is that they’re fire and forget with no way to await their results.

    Changing ImageCaptured to “async Task” will let you await the method and your previous logic will return.

  2. Microsoft’s own sample code located here did exactly the same thing: http://code.msdn.microsoft.com/windowsapps/Web-Authentication-d0485122.

    The user observes that sometimes (usually the first time) after clicking on Launch, nothing happens. I recall sitting in the Microsoft Chicago Office with Windows Expert Jason Fox in May 2012. He observed the wrong behavior, but just clicked the Launch button again and this time it “worked”.

    The weird part was that Jason (did I say he was an expert Microsoft programmer who would decide if I could get a store token?) was not at all curious why he had to click Launch twice to get it to work. I went back home and looked at the sample code again and found the issue:

    Specifically, the call to ResponseDataStream.ReadToEndAsync() is not awaited upon. To fix this, change the PostData function to return TASK, and place an await in front of the call to ReadToEndAsync. Also place an await in front of the call to PostData in the Launch_Click function.

    It turns out there were a lot more problems with the sample application, and I wrote it all up here in May:
    http://w8isms.blogspot.com/2012/05/problems-with-metros-webauthentication.html

    “It is only those who are new to threading that believe it is easy.”
    - John Michael Hauck

    • Christian Jacobsen
      Christian Jacobsen

      Wow that’s remarkably bad of Microsoft to publish such a lousy sample. Thanks for sharing John!

  3. Async void is a code smell in here. To avoid this issue, make sure to return Tasks and push the await as far down the call stack as you can to avoid a bunch of nested awaiting state machines. This is particularly important if your async operation occurs inside of a loop: http://www.thinqlinq.com/Post.aspx/Title/Async-in-loops

  4. You should never write asynchronous void functions, change the return type to Task and see what happens then.

  5. Dan

    Naming convention will help with this: the names of async methods should always end with “Async”. So ImageCaptured should be renamed to ImageCapturedAsync. This makes this problem easy to spot in a code review: “hey, you’re calling an async method but forgot to await it…”

    As mentioned earlier, you should always return a Task from async methods. For “fire and forget” usage, the caller can just assign the task to an unused local variable with a comment explaining “this is fire-and-forget”. (assigning to a local is to prevent a compiler wanring complaining that you forgot to await the call)

  6. Just chiming in here because I was one of the langauge-designers behind the new async+await keywords.

    “async void” should NEVER be used except for event-handlers. (well, there are a few small caveats, but they’re very small).

    We had many design meetings on the topic. We wished to disallow async void methods from the language completely. Unfortunately, the reality is that we’re fitting the new keywords into two existing languages with a lot of existing frameworks, and those existing frameworks expect and demand async void event handlers, so we had to work with them. (If we were designing from a clean slate without any existing frameworks, we probably wouldn’t have had async void at all).

    Although the compiler can’t disallow async voids, I fully expect diagnostic/analysis tools (like Resharper) to take up the slack, to analyze your code, use heuristics to determine whether your async void is an event-handler or not, and in the latter case report it as a serious problem.

    If you used VB, and clicked the “quickfix” button to automatically turn your Sub() into an async method, it automatically turns it into “Async Function() As Task”.

    • Christian Jacobsen
      Christian

      Hi Lucian,
      learned my lesson now, async void will never be written by these fingers again :)
      Thank you for the design background about async/await!

  7. Paul C

    Thanks for posting this, it’s good to know for somebody new to these keywords.

    By the way, I can’t see the code samples (I’m on Chrome 24.0.1312.56 m). I couldn’t see the error in Chrome, so I ran IE 8, which said:

    Message: Unexpected quantifier
    Line: 37
    Char: 35
    Code: 0
    URI: http://www.jayway.com/wp-content/plugins/syntax-highlighter-and-code-prettifier/scripts/shBrushAppleScript.js

  8. Vanya

    Thanx for the tip about not using async void. Could you plz elaborate the difference between async void and async Task.
    eg internal async Task RefreshDataAsync() & internal async void RefreshDataAsync()

    • Christian Jacobsen
      Christian Jacobsen

      Hi Vanya! The difference between the two is that async Task can be awaited while async void can not. So in the example you’ve given, if you don’t need to know when RefreshDataAsync has completed and thus don’t need to perform any actions after it’s completed then it’s safe to use async void, else use async Task. Hope this answers your question.
      Cheers
      Christian

  9. Thanks for the excellent article and the comments also.

    I am trying to gradually port a library to the new async style. I’m sure I’m missing the obvious, but how can I wrap / call a legacy style async pattern

    void OldAsyncMethod(someParms, Action callback)

    with a new method:

    async Task NewMethodAsync(someParms)
    {
    // what goes here ???
    }

    • Trying again with esc seq…

      void OldAsyncMethod(someParms, Action<TResult> callback)

      with a new method:

      async Task<TResult> NewMethodAsync(someParms)
      {
      // what goes here ???
      }

      • Christian Jacobsen
        Christian Jacobsen

        Thanks for your comment Mike!
        About your question: how do you achieve async in the old method? If you’re using the begin/end pattern you can use TaskFactory.FromAsync to migrate to using Task instead. Stephen Toub’s excellent The Task-based Asynchronous Patterndocument has a section that describes how to interop with other .NET async patterns. Hope this solves your problem.

  10. I discovered your blog site on google and check a few of your early posts. Continue to keep up the very good operate. I just additional up your RSS feed to my MSN News Reader. Seeking forward to reading more from you later on!

    • Christian Jacobsen
      Christian Jacobsen

      Thanks! I’ve some new posts in the “pipe” that I hope you will enjoy.
      Cheers,
      Christian

  11. Jacek

    Hi,

    First, yes the title catches attention :) and so I was tempted to read to the bottom.
    Second, could you explain/elaborate why you don’t use await on _handler.ImageCaptured(dto); (line 7 of the “improved” MainPage.cs). I’d say that if a method is declared as async you almost always want to await or otherwise call .Result, .Wait or similar. Am I missing something?

    • Christian Jacobsen
      Christian Jacobsen

      Hi Jacek!
      Thanks for your comment and your question. The reason for not using await on the ImageCaptured call is that async void is not awaitable since there’s no resulting value to await. Think of async void as ‘fire and forget’.
      Cheers,
      Christian

      • Jon

        Couldn’t async Task be used, which wouldn’t have a return value, but could be awaited?

        • Christian Jacobsen
          Christian Jacobsen

          Yes Jon that’s the preferred way to do it. The point I was making is that it is quite easy to return async void (especially for async/await beginners which I was back then) with possibly unexpected results.

  12. Bogdan

    Signature of second method should be:
    async Task ImageCaptured(Dto dto)

    and you should call it with await semantics:
    await _handler.ImageCaptured(dto);

Trackbacks for this post

  1. Windows 8 Developer Links – 2012-10-08 | Dan Rigby

Leave a Reply