Async Programming
ConfigureAwait
C# Programming
.NET
Task Asynchrony

Understanding ConfigureAwait

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

ConfigureAwait is a method on Task and Task<T> in .NET that controls whether the continuation after an await runs on the original synchronization context (such as the UI thread) or on a thread pool thread. Understanding when and why to use ConfigureAwait(false) is essential for writing performant async code and avoiding deadlocks.

How await Works by Default

When you await a task, the runtime captures the current SynchronizationContext (or TaskScheduler) and posts the continuation back to it. In a UI application (WPF, WinForms, MAUI), this means the code after await runs on the UI thread:

csharp
1async Task LoadDataAsync()
2{
3    // Runs on UI thread
4    var data = await httpClient.GetStringAsync("https://api.example.com/data");
5    // Continues on UI thread — can safely update UI
6    textBox.Text = data;
7}

This is convenient for UI code but comes with overhead and potential deadlock risks.

What ConfigureAwait Does

ConfigureAwait takes a single boolean parameter:

  • ConfigureAwait(true): Default behavior. Captures the current context and continues on it.
  • ConfigureAwait(false): Does not capture the context. The continuation runs on any available thread pool thread.
csharp
1async Task LoadDataAsync()
2{
3    // After this await, execution may continue on ANY thread pool thread
4    var data = await httpClient.GetStringAsync("https://api.example.com/data")
5        .ConfigureAwait(false);
6
7    // WARNING: Cannot safely access UI elements here
8    // textBox.Text = data; // This would throw on a non-UI thread
9    return data;
10}

When to Use ConfigureAwait(false)

Library Code

In library methods that do not interact with UI or ASP.NET context, always use ConfigureAwait(false):

csharp
1// Good: library method uses ConfigureAwait(false)
2public async Task<User> GetUserAsync(int id)
3{
4    var response = await _httpClient.GetAsync($"/api/users/{id}")
5        .ConfigureAwait(false);
6
7    var json = await response.Content.ReadAsStringAsync()
8        .ConfigureAwait(false);
9
10    return JsonSerializer.Deserialize<User>(json);
11}

Benefits:

  • Avoids deadlocks when called from synchronous contexts
  • Improves performance by not posting back to the original context
  • Reduces thread contention on the UI thread

Data Access Layers and Services

Any code that does not need access to the calling context should use ConfigureAwait(false):

csharp
1public async Task<List<Product>> GetProductsAsync()
2{
3    using var connection = new SqlConnection(_connectionString);
4    await connection.OpenAsync().ConfigureAwait(false);
5
6    var products = await connection.QueryAsync<Product>("SELECT * FROM Products")
7        .ConfigureAwait(false);
8
9    return products.ToList();
10}

When NOT to Use ConfigureAwait(false)

UI Event Handlers

In UI event handlers where you need to update controls after the await:

csharp
1// DO NOT use ConfigureAwait(false) here
2private async void Button_Click(object sender, EventArgs e)
3{
4    var data = await GetDataAsync(); // Need to return to UI thread
5    textBox.Text = data;             // Updates UI control
6    progressBar.Visible = false;
7}

ASP.NET Core

In ASP.NET Core, there is no SynchronizationContext, so ConfigureAwait(false) has no effect. You can omit it, though including it does no harm:

csharp
1// ASP.NET Core: ConfigureAwait(false) is optional but harmless
2public async Task<IActionResult> GetUsers()
3{
4    var users = await _userService.GetUsersAsync(); // No context to capture
5    return Ok(users);
6}

The Deadlock Problem

The most critical reason for ConfigureAwait(false) is preventing deadlocks. This happens when synchronous code blocks on an async method that tries to return to the captured context:

csharp
1// DEADLOCK EXAMPLE (WPF/WinForms/.NET Framework ASP.NET)
2public void LoadData()
3{
4    // .Result blocks the UI thread
5    var data = GetDataAsync().Result; // DEADLOCK!
6}
7
8async Task<string> GetDataAsync()
9{
10    // Tries to resume on UI thread, but it's blocked by .Result
11    var result = await httpClient.GetStringAsync("https://api.example.com/data");
12    return result;
13}

The fix — use ConfigureAwait(false) in the async method:

csharp
1async Task<string> GetDataAsync()
2{
3    var result = await httpClient.GetStringAsync("https://api.example.com/data")
4        .ConfigureAwait(false); // Resumes on thread pool, no deadlock
5    return result;
6}

The better fix — do not call .Result or .Wait() on async code. Use await all the way up.

ConfigureAwait in .NET 8+

Starting with .NET 8, you can set ConfigureAwait behavior globally using ConfigureAwaitOptions:

csharp
1// .NET 8+: suppress throwing on cancellation
2await task.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
3
4// Combine options
5await task.ConfigureAwait(
6    ConfigureAwaitOptions.ContinueOnCapturedContext |
7    ConfigureAwaitOptions.ForceYielding
8);

Common Pitfalls

  • Inconsistent usage in a call chain: If one method in a chain uses ConfigureAwait(false) but an earlier one does not, the deadlock can still occur. Apply it consistently throughout library code.
  • Using in event handlers: Applying ConfigureAwait(false) in a UI event handler and then trying to update UI elements causes a cross-thread exception.
  • Forgetting after every await: You need ConfigureAwait(false) after each await in a method, not just the first one. Each await independently captures context.
  • ASP.NET Framework vs Core: The classic ASP.NET Framework has a SynchronizationContext and is prone to deadlocks. ASP.NET Core does not. Know which you are targeting.

Summary

ContextUse ConfigureAwait(false)?
Library codeYes, always
Data access / servicesYes
UI event handlersNo
ASP.NET Core controllersOptional (no effect)
ASP.NET Framework controllersYes (prevents deadlocks)
  • ConfigureAwait(false) prevents deadlocks and improves performance in library code
  • Never use it when you need to return to the UI thread
  • Apply it after every await in a method, not just the first
  • The root cause of most deadlocks is calling .Result or .Wait() on async code — prefer await throughout

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.