I want to revisit the async/await keywords in .net 4.5 and talk about some advanced topics.
As I commented previously, there are 3 return types that can be used when creating an asynchronous method in the new .net 4.5 framework:

  • return a Task
  • return a Task<T> (where T is any type)
  • return void

As a rule of thumb, we should only use the first two options. Why? Because of several caveats that appear when using async void methods.

One thing that happens with async void methods is that any exceptions thrown there will not be caught in a try catch statement. For example :

 public async void AsyncMethodThrowsException()
 {
      throw new InvalidOperationException();
 }
 public void CannotBeCaught()
 {
      try
      {
            AsyncMethodThrowsException();
      }
      catch (Exception)
      {
            // The exception is not caught
            throw;
      }
 }

The problem here is that the async pattern creates behind the scenes a state machine to handle asynchronicity, and the Task object is the one that does the background work. When we return void, there is no Task object to give the control back to the caller (it’s a “fire and forget” situation), so then when the exception happens, there is no-one to catch it, and that’s why it doesn’t go up the chain of calls (on a async Task method, the Task object would contain the generated exception in it’s Exception property). These exceptions can be observed using AppDomain.UnhandledException or a similar catch-all event for GUI/ASP.NET applications, but using those events for regular exception handling is a recipe for unmaintainability.
Another problem of async void methods is that since we have no Task to return the control to the caller, we can never know when the async method has completed. Therefore, it’s very difficult to create tests for these type of methods.

There is however one case when async void methods are useful, and that’s the case of asynchronous event handlers.

public async void button1_Click(object sender, EventArgs e)
 {
         await Button1ClickAsync();
 }
 public async Task Button1ClickAsync()
 {
         // Do asynchronous work.
 }

The event handler mechanism built into .net is basically a “fire and forget” scenario, which is basically what an async void method is. We want to handle the event when it happens, and we don’t really care when that finishes, or when the event was generated. We just want a method to be called every time the event is raised, and that’s it, totally independent of the rest of the code.

One subtle trap is passing an async lambda to a method taking an Action parameter; in this case, the async lambda returns void and inherits all the problems of async void methods. As a general rule, async lambdas should only be used if they’re converted to a delegate type that returns Task (for example, Func<Task>).

Another thing to mention is of a common deadlock problem that may happen when blocking on async code. When this happens, it may very difficult to detect, and also, the behavior may not always happen, so we should really try to avoid it (by avoiding async void methods!). The problem can be seen here:

public static class Deadlock
{

   private static async Task DelayAsync()
   {
        await Task.Delay(1000);
   }
   // This method causes a deadlock when called in a GUI or ASP.NET context.

   public static void Test()
   {
        // Start the delay.
        var delayTask = DelayAsync();
        // Wait for the delay to complete.
        delayTask.Wait();
   }
}

The root cause of this deadlock is due to the way await handles contexts. By default, when an incomplete Task is awaited, the current “context” is captured and used to resume the method when the Task completes. This “context” is the current SynchronizationContext unless it’s null, in which case it’s the current TaskScheduler. GUI and ASP.NET applications have a SynchronizationContext that permits only one chunk of code to run at a time. When the await completes, it attempts to execute the remainder of the async method within the captured context. But that context already has a thread in it, which is (synchronously) waiting for the async method to complete. They’re each waiting for the other, causing a deadlock.

Console applications don’t cause this deadlock. They have a thread pool SynchronizationContext instead of a one-chunk-at-a-time SynchronizationContext, so when the await completes, it schedules the remainder of the async method on a thread pool thread. The method is able to complete, which completes its returned task, and there’s no deadlock.

The best solution to this problem is to allow async code to grow naturally through the codebase. The Main method for a console application is one of the few situations where code may block on an asynchronous method.

class Program
{
     static void Main()
     {
          MainAsync().Wait();
     }

     static async Task MainAsync()
     {
        try
        {
          // Asynchronous implementation.
          await Task.Delay(1000);
        }
        catch (Exception ex)
        {
           // Handle exceptions.
        }
     }
}

Finally, a couple of rules that can be used as a cheat sheet when working with the async / await pattern :

The “Async Way” of Doing Things

To Do This … Instead of This … Use This
Retrieve the result of a background task Task.Wait or Task.Result await
Wait for any task to complete Task.WaitAny await Task.WhenAny
Retrieve the results of multiple tasks Task.WaitAll await Task.WhenAll
Wait a period of time Thread.Sleep await Task.Delay