ConfigureAwait
ConfigureAwait controls where code resumes after an await. Understanding it is crucial for library authors and for avoiding deadlocks.
The Synchronization Context Problem
In UI applications and ASP.NET (classic), there's a "synchronization context" that captures the current thread:
csharp1// In a UI application2async void Button_Click(object sender, EventArgs e)3{4 // Running on UI thread5 var data = await FetchDataAsync();67 // After await, BACK on UI thread (captured context)8 lblResult.Text = data; // Safe - on UI thread9}
How ConfigureAwait Works
csharp1// Default: Resume on captured context2await SomeMethodAsync();34// ConfigureAwait(true): Same as default5await SomeMethodAsync().ConfigureAwait(true);67// ConfigureAwait(false): Resume on ANY thread8await SomeMethodAsync().ConfigureAwait(false);
The Deadlock Problem
Without ConfigureAwait(false), you can cause deadlocks:
csharp1// DEADLOCK in UI/ASP.NET classic!2public string GetData()3{4 // This blocks the UI thread5 return GetDataAsync().Result; // DEADLOCK!6}78private async Task<string> GetDataAsync()9{10 // This tries to resume on UI thread (which is blocked)11 await httpClient.GetStringAsync(url);12 return "data";13}
The deadlock occurs because:
GetData()blocks the UI thread waiting for the taskGetDataAsync()awaits and captures the UI context- After the HTTP call, it tries to resume on UI thread
- UI thread is blocked waiting, so it can never resume
Solution: ConfigureAwait(false)
csharp1private async Task<string> GetDataAsync()2{3 // Resume on any available thread pool thread4 await httpClient.GetStringAsync(url).ConfigureAwait(false);5 return "data"; // May run on different thread6}
When to Use ConfigureAwait(false)
Library Code
Always use ConfigureAwait(false) in library code:
csharp1// In a library/NuGet package2public async Task<Result> ProcessAsync()3{4 var data = await FetchAsync().ConfigureAwait(false);5 var parsed = await ParseAsync(data).ConfigureAwait(false);6 return await TransformAsync(parsed).ConfigureAwait(false);7}
Why? Library code shouldn't assume a specific context. The caller may not have one, or capturing it may cause issues.
Application Code
In application code (UI, web controllers), it depends:
csharp1// UI code - DON'T use ConfigureAwait(false) if you need UI access2async void Button_Click(object sender, EventArgs e)3{4 var data = await FetchDataAsync(); // No ConfigureAwait5 lblResult.Text = data; // Need to be on UI thread!6}78// ASP.NET Core - Context doesn't matter (no SynchronizationContext)9public async Task<IActionResult> GetData()10{11 var data = await _service.GetDataAsync(); // ConfigureAwait optional12 return Ok(data);13}
Modern .NET: Less Relevant
In .NET Core and .NET 5+:
- ASP.NET Core has no SynchronizationContext
- Console apps have no SynchronizationContext
- ConfigureAwait(false) is mostly a no-op
Still important for:
- Libraries that may be used in WPF/WinForms
- .NET Framework applications
- Code that might block on async (though you shouldn't)
ConfigureAwait in Loops
Apply to each await:
csharp1async Task ProcessItemsAsync(List<string> items)2{3 foreach (var item in items)4 {5 // Each await needs ConfigureAwait6 var data = await FetchAsync(item).ConfigureAwait(false);7 await ProcessAsync(data).ConfigureAwait(false);8 }9}
ConfigureAwait with WhenAll/WhenAny
csharp1// ConfigureAwait on the WhenAll, not individual tasks2var tasks = urls.Select(url => FetchAsync(url));3await Task.WhenAll(tasks).ConfigureAwait(false);
What ConfigureAwait Does NOT Do
csharp1// MYTH: ConfigureAwait(false) makes the awaited method run on background thread2await DoWorkAsync().ConfigureAwait(false); // DoWorkAsync runs wherever it runs34// TRUTH: It only affects where code AFTER the await resumes
Code Analysis Rules
Use analyzers to enforce ConfigureAwait:
xml1<!-- In .csproj for library projects -->2<PropertyGroup>3 <AnalysisMode>Recommended</AnalysisMode>4</PropertyGroup>
Common rule: CA2007 - Consider calling ConfigureAwait on the awaited task.
Summary Table
| Scenario | Recommendation |
|---|---|
| Library code | Always use ConfigureAwait(false) |
| ASP.NET Core | Optional (no context) |
| Console apps | Optional (no context) |
| WPF/WinForms - need UI | Don't use ConfigureAwait(false) |
| WPF/WinForms - no UI access | Use ConfigureAwait(false) |
Key Takeaways
ConfigureAwait(false)prevents capturing sync context- Prevents deadlocks when blocking on async code
- Always use in library code
- Less important in .NET Core/5+ (no context in ASP.NET Core)
- Only affects where code resumes, not where it runs
- Apply to every
awaitin a method if using it