Essential C# Async Await  Suggest an edit

Blocking on async code can cause deadlocks

It is a bad practice to block on async calls as it can cause deadlocks in environments that have a synchronization context. The best practice is to use async/await “all the way down.” For example, the following Windows Forms code causes a deadlock:

private async Task<bool> TryThis()
{
    Trace.TraceInformation("Starting TryThis");
    await Task.Run(() =>
    {
        Trace.TraceInformation("In TryThis task");
        for (int i = 0; i < 100; i++)
        {
            // This runs successfully - the loop runs to completion
            Trace.TraceInformation("For loop " + i);
            System.Threading.Thread.Sleep(10);
        }
    });
		// This never happens due to the deadlock
    Trace.TraceInformation("About to return");
    return true;
}

// Button click event handler
private void button1_Click(object sender, EventArgs e)
{
    // .Result causes this to block on the asynchronous call
    bool result = TryThis().Result;
    // Never actually gets here
    Trace.TraceInformation("Done with result");
}

Essentially, once the async call completes, it waits for the synchronization context to become available. However, the event handler “holds on” to the synchronization context while it’s waiting for the TryThis() method to complete, thus causing a circular wait.

To fix this, code should be modified to

private async void button1_Click(object sender, EventArgs e)
{
  bool result = await TryThis();
  Trace.TraceInformation("Done with result");
}

Note: event handlers are the only place where async void should be used (because you can’t await an async void method).


Table Of Contents
22 Enum
25 GUID
107 Stream
108 Timers
112 Async Await
  ↑ ↓ to navigate     ↵ to select     Esc to close