Armadilhas comuns
ResumoAssincronismo mal usado gera deadlocks, exceções engolidas e uso concorrente indevido do
DbContext. Este tópico reúne as armadilhas mais frequentes e como evitá-las.
1. Objetivos de Aprendizagem#
- Reconhecer e evitar deadlocks por bloqueio síncrono.
- Entender por que
DbContextnão é thread-safe. - Usar
Task.WhenAllcorretamente (e quando não usar).
2. Pré-requisitos#
- Refatoração assíncrona (tópico 28.2).
3. Conceito#
Assincronismo tem armadilhas sutis. O porquê de estudá-las: os bugs aparecem sob carga, não em testes locais simples, e são difíceis de diagnosticar.
4. Mão na Massa#
4.1. Setup#
Conceitual, com exemplos comentados.
4.2. Implementação Passo a Passo#
- Deadlock por bloqueio:
// RUIM: pode travar
var resultado = _service.GetAsync().Result;
// BOM
var resultado = await _service.GetAsync();
DbContextnão é thread-safe — não dispare consultas paralelas no mesmo contexto:
// RUIM: duas consultas concorrentes no mesmo DbContext
var t1 = _repo.Categoria.GetAllAsync(false);
var t2 = _repo.Produto.GetAllAsync(false);
await Task.WhenAll(t1, t2); // pode lançar InvalidOperationException
// BOM: sequencial no mesmo contexto, ou contextos separados via IDbContextFactory
var categorias = await _repo.Categoria.GetAllAsync(false);
var produtos = await _repo.Produto.GetAllAsync(false);
- Exceção engolida por
async void:
// RUIM: exceção não pode ser capturada por quem chama
public async void Processar() { throw new InvalidOperationException(); }
// BOM
public async Task ProcessarAsync() { /* ... */ }
4.3. Executando#
Reproduza o cenário do DbContext concorrente para ver a InvalidOperationException (A second operation was started on this context...).
5. Exemplo Completo#
Quando Task.WhenAll é adequado: chamadas independentes a serviços externos (HTTP), não ao mesmo DbContext.
var t1 = _httpClient.GetAsync(urlA);
var t2 = _httpClient.GetAsync(urlB);
await Task.WhenAll(t1, t2);
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
Sempre await em vez de .Result/.Wait() | Bloquear a thread e arriscar deadlock |
Uma operação por vez em um DbContext | Paralelizar consultas no mesmo contexto |
Task em vez de async void | Perder exceções silenciosamente |
Atenção
IDbContextFactory<T>permite criar contextos independentes para paralelismo real, mas cada contexto tem seu próprio ciclo de vida — descarte-os corretamente.
7. Segurança e Produção#
- Deadlocks e vazamento de conexões se manifestam como esgotamento do pool sob carga — monitore contadores de conexão e latência.
8. Exercícios#
- Fácil: corrija um trecho que usa
.Result. - Médio: reproduza e explique o erro de
DbContextconcorrente. - Desafio: implemente paralelismo seguro com
IDbContextFactory.
9. Resumo#
Evite .Result/.Wait(), nunca use o mesmo DbContext em paralelo e prefira Task a async void. Task.WhenAll só para operações realmente independentes.
10. Próximos Passos#
Módulo 29: Action Filters para lógica transversal (cross-cutting).
11. Referências#
- Microsoft Learn — Boas práticas de desempenho no ASP.NET Core
- Microsoft Learn — DbContext e threading
- Microsoft Learn — IDbContextFactory