async/await pitfalls
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
-
Use await instead of .Result
string data = ____ FetchAsync();Show answer
string data = await FetchAsync(); -
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.
-
Match the concepts
- .Result
- May cause deadlock
- await
- Safe waiting
- ConfigureAwait(false)
- Does not capture context
- async void
- Only for event handlers