DEV Community

Cover image for Async/Await na prática: por que seu código 'assíncrono' ainda está lento?
Filipi Firmino
Filipi Firmino

Posted on

Async/Await na prática: por que seu código 'assíncrono' ainda está lento?

Você colocou async no método, await em tudo quanto é canto, a IDE parou de reclamar... mas o sistema continua lento. Dá até vontade de gritar: “Tô usando async! Por que isso ainda parece síncrono com insônia?”

Calma, dev. Bora entender onde tá o bug invisível (e como evitá-lo no mundo real do .NET).


Primeiro, um alerta: async/await não é magia negra ⚠️

Sim, a sintaxe parece um encantamento de Hogwarts: await AbrirConexaoComBancoAsync();
Mas por trás disso, tem threads, tasks, contextos de sincronização, e um compilador trabalhando mais do que o estagiário em semana de entrega.

E é aí que mora o perigo: muita gente usa async/await achando que está paralelizando tudo, quando na real está só empilhando promessas mal resolvidas.


Entendendo a armadilha: o falso assíncrono

Veja este exemplo clássico de cilada:

public async Task<List<Usuario>> GetUsuariosAsync()
{
    var lista = new List<Usuario>();

    foreach (var id in listaDeIds)
    {
        var usuario = await _servicoUsuarios.BuscarPorIdAsync(id);
        lista.Add(usuario);
    }

    return lista;
}
Enter fullscreen mode Exit fullscreen mode

Você olha isso e pensa:

Olha que lindo, tudo assíncrono!

Mas não. Tá tudo sequencial. Um await só roda depois que o outro termina.
Isso é o equivalente a usar Uber pra ir pra padaria da esquina. Funciona? Funciona. Mas tá longe de ser eficiente.

A solução? Task.WhenAll na sua vida 🧠

Se o serviço permite chamadas em paralelo, faça isso:

public async Task<List<Usuario>> GetUsuariosAsync()
{
    var tarefas = listaDeIds
        .Select(id => _servicoUsuarios.BuscarPorIdAsync(id));

    var resultados = await Task.WhenAll(tarefas);

    return resultados.ToList();
}
Enter fullscreen mode Exit fullscreen mode

Agora sim: todas as requisições estão rolando ao mesmo tempo, aproveitando o que a programação assíncrona tem de melhor.

Outro vilão oculto: o tal do ConfigureAwait(false)

Se você tá desenvolvendo em ASP.NET Core, usar ou não usar ConfigureAwait(false) pode ser a diferença entre uma app escalável ou uma panela de pressão explodindo.

Por padrão, await tenta capturar o contexto de sincronização original (tipo, o contexto do thread do request).
Mas no ASP.NET Core, você não precisa disso. E evitar essa captura libera a thread mais cedo.

Exemplo:

await _algumaCoisa.FazAlgoAsync().ConfigureAwait(false);
Enter fullscreen mode Exit fullscreen mode

Menos overhead. Mais performance. Mais paz.

Tá, mas quando NÃO usar paralelismo?

Nem todo async precisa ser paralelo.

  • Se os métodos compartilham estado (tipo, gravam na mesma lista), melhor evitar.
  • Se sua fonte de dados limita chamadas concorrentes (como alguns bancos ou APIs), paralelismo pode prejudicar.
  • Se o serviço for "assíncrono em aparência, mas síncrono por dentro", também não adianta muito...

Ou seja: assíncrono é ótimo — mas nem tudo que brilha é Task.Run().


Moral da thread

Async não resolve tudo, mas bem usado é poder puro ⚙️

O uso de async/await no C# é uma das maiores evoluções da linguagem.Mas usá-los de forma ingênua é como dirigir uma Ferrari com freio de mão puxado: bonito por fora, frustrante por dentro.

Seja esperto. Entenda o que tá rodando por baixo.
Meça, monitore, e use as ferramentas com propósito.

Seu sistema, seu time e seu cliente vão agradecer (e talvez até o seu GC também).


P.S.:
Se você leu até aqui e pensou “puts, eu faço isso no meu código😬” — respira. Refatora.
E compartilha esse artigo com o time antes que mais alguém invente de colocar async void no Controller.

Top comments (0)