ConfigureAwait e contexto de sincronização
Entenda SynchronizationContext, use ConfigureAwait(false) em código de biblioteca e evite deadlocks em fluxos assíncronos.
ConfigureAwait e contexto de sincronização é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.
O Que é SynchronizationContext?
SynchronizationContext controla como o código de continuação após um await é agendado. Em aplicativos de UI (WPF, WinForms), ele garante que as atualizações da UI ocorram na thread da UI. No ASP.NET Core, não existe um contexto de sincronização (ele foi removido). Compreender isso é essencial para escrever código assíncrono correto.
Como ConfigureAwait funciona
.ConfigureAwait(false) instrui o mecanismo de espera a NÃO capturar nem retomar no SynchronizationContext atual. A continuação é executada em qualquer thread disponível do pool de threads.
// Default: captures context (resumes on original context)
var data = await FetchDataAsync();
UpdateUI(data); // runs on UI thread — correct for UI apps
// ConfigureAwait(false): doesn't capture context
var data2 = await FetchDataAsync().ConfigureAwait(false);
// Runs on any thread — DON'T access UI thread-only objects hereO padrão clássico de impasse
Bloquear uma Task com .Result ou .Wait() enquanto mantém um contexto de sincronização causa um impasse clássico — a continuação espera pelo contexto que está bloqueado aguardando a continuação.
// DEADLOCK in WPF/WinForms or ASP.NET classic:
string result = GetDataAsync().Result; // blocks UI thread
// GetDataAsync() tries to resume on UI thread after await
// UI thread is blocked by .Result
// DEADLOCK!
// Fix 1: await properly (never block)
string result = await GetDataAsync();
// Fix 2: use ConfigureAwait(false) in the library
public async Task<string> GetDataAsync()
=> await client.GetStringAsync(url).ConfigureAwait(false);ConfigureAwait(false) em bibliotecas
O código de uma biblioteca que não depende de contexto deve sempre usar ConfigureAwait(false) em toda operação de espera. Isso evita impasses quando os chamadores usam a biblioteca em um ambiente sensível ao contexto.
// Library code — always ConfigureAwait(false)
public async Task<User?> GetUserAsync(int id)
{
var json = await client
.GetStringAsync($"/users/{id}")
.ConfigureAwait(false);
var user = JsonSerializer.Deserialize<User>(json);
return user;
}
// Application code (has context) — leave ConfigureAwait default
public async Task LoadUserProfileAsync(int id)
{
var user = await userService.GetUserAsync(id); // context captured
ProfileLabel.Text = user?.Name; // safe to update UI
}ASP.NET Core: sem SynchronizationContext
O ASP.NET Core deliberadamente não tem SynchronizationContext. As continuações são executadas no pool de threads. ConfigureAwait(false) tecnicamente não faz nada, mas ainda é um bom hábito para código compartilhado com outras estruturas.
// In ASP.NET Core, both are equivalent:
var data = await service.GetAsync(); // fine
var data2 = await service.GetAsync().ConfigureAwait(false); // also fine
// Context-free means: no deadlock risk from .Result in ASP.NET Core
// BUT: mixing with libraries that assume a context is still risky
// BEST PRACTICE: still use ConfigureAwait(false) in library codeAgendamento no pool de threads versus no contexto
Sem um contexto, as continuações são agendadas pelo TaskScheduler — normalmente pelo ThreadPoolTaskScheduler. Com um contexto de interface do usuário, elas são enviadas para o loop de mensagens da interface.
// Check current synchronization context:
Console.WriteLine(SynchronizationContext.Current?.GetType().Name
?? "No context (thread pool)");
// In a WPF event handler: "DispatcherSynchronizationContext"
// In ASP.NET Core: null (no context)
// In a unit test with xUnit: "AsyncTestSyncContext"
// After ConfigureAwait(false):
await Task.Delay(1).ConfigureAwait(false);
Console.WriteLine(SynchronizationContext.Current?.GetType().Name
?? "No context"); // null — context was abandonedConfigureAwait em loops
Ao aguardar dentro de um loop, a continuação de cada iteração é executada no contexto capturado, a menos que ConfigureAwait(false) seja usado. No código de bibliotecas, aplique-o consistentemente em toda operação de espera do loop.
// Library loop: ConfigureAwait(false) on every await
public async Task ProcessBatchAsync(IEnumerable<int> ids)
{
foreach (var id in ids)
{
var item = await FetchItemAsync(id).ConfigureAwait(false);
await SaveItemAsync(item).ConfigureAwait(false);
}
}ValueTask e ConfigureAwait
ValueTask também oferece suporte a ConfigureAwait(false). Aplique-o consistentemente, seguindo as mesmas regras usadas para Task.
public async ValueTask<string> GetFromCacheAsync(string key)
{
// ValueTask with ConfigureAwait(false)
var raw = await _store.GetAsync(key).ConfigureAwait(false);
return raw ?? "";
}Task.Run para evitar o contexto
Use Task.Run para transferir o trabalho limitado pela CPU para o pool de threads, garantindo que ele nunca seja executado em uma thread que mantém um contexto, como a thread da interface do usuário.
// WRONG in UI app: blocks UI thread
void ButtonClick(object? sender, EventArgs e)
{
var result = HeavyComputation(); // blocks UI
Label.Text = result;
}
// CORRECT: offload to thread pool
async void ButtonClick(object? sender, EventArgs e)
{
var result = await Task.Run(() => HeavyComputation());
// Continuation captured UI context: safe to update UI
Label.Text = result;
}Na prática: biblioteca HttpClient com ConfigureAwait
Um wrapper de cliente HTTP de produção usa ConfigureAwait(false) em todo o código para funcionar com segurança quando chamado de qualquer contexto.
public class ApiClient
{
private readonly HttpClient _http;
private readonly JsonSerializerOptions _opts = new(JsonSerializerDefaults.Web);
public ApiClient(HttpClient http) => _http = http;
public async Task<T> GetAsync<T>(string path, CancellationToken ct = default)
{
using var response = await _http
.GetAsync(path, ct)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
return await response.Content
.ReadFromJsonAsync<T>(_opts, ct)
.ConfigureAwait(false)
?? throw new InvalidOperationException("Null response");
}
}Verificação rápida
Por que chamar .Result em uma Task em um aplicativo WPF ou WinForms frequentemente causa um impasse?
Recapitulação: ConfigureAwait e SynchronizationContext
Principais conclusões:
SynchronizationContext: agenda continuações de volta para uma thread específica, como a thread da interface do usuárioConfigureAwait(false): abandona a captura do contexto e continua no pool de threads- Código de bibliotecas: use sempre
ConfigureAwait(false)para evitar impasses - Código da aplicação com interface do usuário: use o
awaitpadrão para retornar à thread da interface - ASP.NET Core: não há SynchronizationContext — mas ainda é uma boa prática para bibliotecas compartilhadas
- NUNCA bloqueie uma
Taskcom.Result/.Wait()em aplicações sensíveis ao contexto
Perguntas Frequentes
A aula “ConfigureAwait e contexto de sincronização” é grátis?
Sim — o texto completo de “ConfigureAwait e contexto de sincronização” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.
O que vou aprender em “ConfigureAwait e contexto de sincronização”?
Entenda SynchronizationContext, use ConfigureAwait(false) em código de biblioteca e evite deadlocks em fluxos assíncronos. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar C# Academy?
Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “ConfigureAwait e contexto de sincronização”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de C# Academy?
Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- IAsyncEnumerable e await foreach
- System.Threading.Channels
- ValueTask e como evitar alocações
- ConfigureAwait e contexto de sincronização