CodeBlanks Logo Icon CodeBlanks

async/await pitfalls

C# course · Advanced · Lesson 5/83

Learn common pitfalls like deadlock and sync-over-async.

The deadlock pitfall

Waiting for an async method with .Result or .Wait() from synchronous code can deadlock. In single-threaded contexts like ASP.NET or WinForms, the async method's continuation waits for the synchronously blocking thread; the two wait for each other.

The fix: use await instead of .Result. ConfigureAwait(false) also helps by avoiding context capture. Principle: do not call async methods from synchronous code.

ConfigureAwait(false)

ConfigureAwait(false) tells await not to capture the context. This is recommended in library code: you do not need to return to the caller's context, the continuation can run on any thread. This reduces deadlock risk.

Usage: await DoAsync().ConfigureAwait(false);. In UI code, keep the default ConfigureAwait(true) so that UI updates run on the UI thread.

Exercise Fix the deadlock with await

Define static async Task<string> Fetch(). In its body write await Task.CompletedTask and return "Data";. Make Main a static async Task Main. Read the result with string data = await Fetch(); and print it with Console.WriteLine(data);.

Show example solution
In Fetch body write await Task.CompletedTask and return "Data". Make Main static async Task Main() and write string data = await Fetch(); Console.WriteLine(data);
Example solution:
using System;
using System.Threading.Tasks;

class Program
{
    static async Task<string> Fetch()
    {
        await Task.CompletedTask;
        return "Data";
    }

    static async Task Main()
    {
        string data = await Fetch();
        Console.WriteLine(data);
    }
}

Test yourself

  1. Use await instead of .Result

    string data = ____ FetchAsync();
    Show answer
    string data = await FetchAsync();
  2. Which one can cause a deadlock?

    • await Task.Delay(100)
    • await Task.Run(() => Work())
    • string s = await FetchAsync();
    • Task.Run(() => Work()).GetAwaiter().GetResult()
    Show answer

    Task.Run(() => Work()).GetAwaiter().GetResult() — Correct. Synchronously blocking on async code can deadlock.

  3. Match the concepts

    .Result
    May cause deadlock
    await
    Safe waiting
    ConfigureAwait(false)
    Does not capture context
    async void
    Only for event handlers

All lessons in Advanced