códigomadrugada

c#async.netintermediárioperformance

Como funciona async/await em C#: guia para iniciantes

Entenda de vez o que é programação assíncrona em C#, como async e await funcionam por baixo dos panos, quando usar e quando evitar — com exemplos práticos.

Diagrama mostrando execução síncrona vs assíncrona em C#
Código da Madrugada1 de maio de 20255 min de leitura

Async e await estão em praticamente todo código C# moderno. Você vê métodos com async Task, await em chamadas ao banco de dados, às APIs externas, à leitura de arquivos.

Mas o que realmente acontece quando você coloca essas palavras-chave? Por que o código não trava? O que é uma Task? Quando você deve ou não usar async?

Vamos resolver isso de vez.

O problema que async/await resolve

Imagine um servidor web recebendo 100 requisições simultâneas. Cada requisição faz uma consulta ao banco de dados que demora 50ms.

Em um modelo síncrono, cada requisição ocupa uma thread enquanto espera o banco responder. 100 requisições = 100 threads bloqueadas esperando o banco. O servidor fica preso, novos pedidos ficam na fila.

Em um modelo assíncrono, quando a thread envia a consulta ao banco e aguarda, ela fica livre para atender outra requisição. Quando o banco responde, qualquer thread disponível retoma o processamento. O resultado: um servidor com poucas threads pode atender milhares de requisições simultâneas.

Esse é o poder do async — não velocidade bruta, mas eficiência de recursos.

O que é uma Task?

Antes de entender async/await, você precisa entender Task.

Task é a representação de uma operação assíncrona em andamento. É como uma promessa: "essa operação está acontecendo, e quando terminar, eu te aviso".

Task minhaTask = AlgumTrabalhoAssincrono();

Existe também a versão genérica Task<T>, para quando a operação retorna um resultado:

Task<string> taskComResultado = BuscarDadosDoServidor();
string resultado = await taskComResultado;

Como async e await funcionam

A palavra-chave async marca um método como assíncrono. Sozinha, ela não faz nada de especial — é o await que tem o poder.

Quando o compilador vê await, ele transforma o código em uma máquina de estados. Basicamente:

  1. A execução chega no await
  2. A thread que estava executando é liberada
  3. A operação assíncrona acontece (banco responde, HTTP retorna, arquivo é lido)
  4. Uma thread disponível retoma a execução depois do await

Do ponto de vista do código, parece que a execução "pausa" e depois continua. Do ponto de vista do sistema, a thread foi embora e outra voltou — mas você como desenvolvedor não precisa pensar nisso.

Exemplo prático: sem async vs. com async

Versão síncrona — bloqueia a thread:

public string BuscarUsuario(int id)
{
    var response = httpClient.GetAsync($"/api/usuarios/{id}").Result; // BLOQUEIO
    return response.Content.ReadAsStringAsync().Result; // BLOQUEIO
}

Problema: .Result bloqueia a thread até o HTTP responder. Em um servidor com muitas requisições simultâneas, isso desperdiça recursos.

Versão assíncrona — libera a thread:

public async Task<string> BuscarUsuarioAsync(int id)
{
    var response = await httpClient.GetAsync($"/api/usuarios/{id}");
    return await response.Content.ReadAsStringAsync();
}

A diferença: com await, a thread é liberada enquanto espera o HTTP. O servidor pode atender outras requisições nesse tempo.

Regras que você precisa seguir

1. Async sobe até o topo

Quando você usa await dentro de um método, esse método precisa ser async. E quem chama esse método provavelmente também precisará de await. Async se propaga pela cadeia de chamadas.

// Se isso é async...
public async Task<Pedido> BuscarPedidoAsync(int id) { ... }

// ...então quem chama também precisa de await:
public async Task ProcessarAsync()
{
    var pedido = await BuscarPedidoAsync(123);
    // ...
}

2. Nunca use .Result ou .Wait() em código async

// ERRADO — pode causar deadlock
var resultado = MinhaOperacaoAsync().Result;

// CERTO
var resultado = await MinhaOperacaoAsync();

Misturar síncrono e assíncrono assim pode causar deadlock — o programa trava para sempre esperando ele mesmo.

3. Sufixo "Async" é convenção

Métodos assíncronos que retornam Task ou Task<T> devem ter o sufixo Async no nome: GetAsync, SaveAsync, ProcessarPedidoAsync. Isso ajuda quem lê o código a saber que precisa usar await.

4. Use ConfigureAwait(false) em bibliotecas

Se você está escrevendo uma biblioteca (não uma aplicação), use ConfigureAwait(false) nas suas tasks para evitar problemas com o contexto de sincronização:

var dados = await BuscarDadosAsync().ConfigureAwait(false);

Em aplicações ASP.NET Core e console apps, isso geralmente não é necessário — mas é boa prática em código de biblioteca.

Quando NÃO usar async

Async tem overhead. Para operações puramente em memória — ordenar uma lista, calcular um valor, manipular strings — async não ajuda e adiciona complexidade desnecessária.

Use async quando houver I/O real:

  • Consultas ao banco de dados
  • Chamadas HTTP para APIs externas
  • Leitura e escrita de arquivos
  • Envio de e-mails, mensagens para filas (RabbitMQ, Azure Service Bus)

Para operações CPU-bound intensas (compressão de vídeo, cálculo matemático pesado), considere Task.Run() para mover para uma thread separada — mas isso é diferente de async de I/O.

Exemplo real: controller de API

[HttpGet("{id}")]
public async Task<IActionResult> GetPedido(int id)
{
    var pedido = await _repository.BuscarPorIdAsync(id);

    if (pedido is null)
        return NotFound();

    return Ok(pedido);
}

Esse é o padrão que você vai ver em qualquer API ASP.NET Core moderna. O controller é async, a chamada ao repositório é aguardada, e o resultado é retornado. Simples, eficiente, escalável.

Domine programação assíncrona em C#

Curso completo com projetos reais, em português. Do básico ao async avançado.

Ver cursos

Conclusão

Async/await não é magia — é o compilador transformando seu código em máquinas de estado que permitem que threads sejam usadas de forma eficiente enquanto esperam operações de I/O.

O resumo prático:

  • Use async Task quando o método tem operações de I/O
  • Sempre use await em vez de .Result ou .Wait()
  • Async se propaga — se uma camada é async, as camadas acima também devem ser
  • Não use async onde não há I/O — é overhead desnecessário

Com esse modelo mental, async/await deixa de ser um mistério e vira uma ferramenta natural no seu código C#.

Perguntas frequentes

Async/await deixa o código mais rápido?
Não necessariamente mais rápido — mas mais eficiente. Em operações de I/O (banco de dados, HTTP, arquivos), async libera a thread enquanto espera, permitindo que ela atenda outras requisições. O resultado prático em servidores é suportar muito mais requisições simultâneas com o mesmo hardware.
Devo usar async em todo lugar?
Não. Async tem overhead — uma task simples síncrona pode ser mais eficiente do que uma assíncrona. Use async quando houver I/O real: chamadas HTTP, consultas ao banco, leitura de arquivos. Para operações puramente em memória e CPU, async não ajuda e pode atrapalhar.
O que é deadlock em async e como evitar?
Deadlock acontece quando você mistura código síncrono e assíncrono de forma errada — especialmente usando .Result ou .Wait() em Tasks dentro de um contexto que tem SynchronizationContext (como ASP.NET Framework antigo). A solução: não bloqueie tasks. Use await até o fim da cadeia. Se precisar chamar código async de forma síncrona, use ConfigureAwait(false) ou uma nova thread.
Qual a diferença entre Task e ValueTask?
Task aloca sempre um objeto no heap. ValueTask é uma struct que evita a alocação quando o resultado já está disponível sincronamente — útil em cenários de alta performance com cache. Para código comum, use Task. Para bibliotecas de alta performance, considere ValueTask.

Artigos relacionados

c#iniciantes

O que é C# e por que aprender em 2026?

C# é uma linguagem moderna, fortemente tipada e versátil, criada pela Microsoft. Neste artigo você vai entender o que ela é, onde é usada e por que pode ser a porta de entrada ideal para sua carreira dev.

5 minLer →
c#.net

C# 14 e .NET 10: o que mudou para iniciantes

C# 14 e .NET 10 LTS chegaram em novembro de 2025. Se você tá no meio de um curso ou projeto, precisou parar tudo pra reaprender? A resposta é não. Mas tem novidades que valem conhecer agora.

6 minLer →

Quer aprender C# do zero ao avançado?

Cursos práticos em português, com projetos reais e acesso vitalício.

Conhecer o curso de C#