async/await compoe Tasks para que trabalho I/O-bound (HTTP, DB, arquivo, sockets) rode sem bloquear threads. O compilador gera uma state machine que pausa no await e resume quando a Task completa.

Como funciona a state machine

Quando voce marca um metodo com async, o compilador transforma o corpo em uma state machine. Cada await e um ponto de suspensao: se a operacao I/O ainda nao completou, a thread e liberada e o metodo retorna uma Task. Quando o I/O completa, a continuacao e agendada e o metodo resume de onde parou.

I/O pendingSync completeCall async methodExecute sync partawait I/OYield threadI/O completesResume after awaitContinue sync
Otimizacao "fast path": se a Task awaitada ja esta completa no momento do await (cache hit, sync completion, ValueTask sincrono), o compilador nao aloca a state machine no heap nem agenda continuacao β€” o metodo executa todo na stack como codigo sincrono normal. Essa e a razao por que ValueTask<T> existe: quando o caso comum e "ja temos a resposta", evita alocar uma Task de heap por chamada. Em hot path com cache hit, e a diferenca entre alocar zero ou alocar uma Task por request.

I/O-bound vs CPU-bound

A distincao e fundamental para escolher entre async/await e Task.Run.

TipoExemplosAbordagemBloqueia thread?
I/O-boundHTTP, banco de dados, arquivo, socketasync/awaitNao
CPU-boundCalculo pesado, criptografia, parsingTask.Run (offload para ThreadPool)Sim (na worker thread)

Task vs ValueTask

AspectoTask<T>ValueTask<T>
AlocacaoSempre aloca no heapStack quando sincrono, heap quando async
Quando usarPadrao para a maioria dos cenariosHot path com resultado sincrono frequente
Pode awaitar multiplas vezes?SimNao
RegraUse por padraoUse somente quando mediu que ajuda

Retorno de metodos async

RetornoQuando usarPode awaitar?Propaga excecao?
TaskMetodo async sem retornoSimSim
Task<T>Metodo async com retornoSimSim
async voidSomente event handlersNaoNao (crash)
Por que nao se deve usar async void?
async void nao retorna Task, entao o chamador nao pode awaitar nem observar excecoes. Se uma excecao ocorre, ela vai para o SynchronizationContext e geralmente crasha o processo. A unica excecao e em event handlers (ex: Button_Click), onde a assinatura e imposta pelo framework. Em qualquer outro caso, retorne Task.
O que acontece se voce esquecer de awaitar uma Task?
A Task roda em "fire-and-forget" β€” excecoes sao silenciosamente engolidas (nao propagam para o chamador). Em .NET 4+, Tasks nao observadas nao crasham o processo, mas voce perde o controle de erros, ordering e lifetime. O compilador emite warning CS4014 para alertar. Sempre awaite ou armazene a Task explicitamente com descarte (_ = DoAsync()).
ConfigureAwait(false) β€” quando e por que?
Por padrao, await captura o SynchronizationContext e resume no mesmo contexto (ex: UI thread). ConfigureAwait(false) pula essa captura β€” a continuacao roda em qualquer thread do pool. Use em codigo de biblioteca para evitar deadlocks e reduzir overhead. ASP.NET Core nao tem SynchronizationContext de jeito nenhum β€” ConfigureAwait(false) dentro de uma controller/minimal API e um no-op (nao quebra nada, so nao faz nada). Em WPF/WinForms/MAUI/ASP.NET Framework, sem ConfigureAwait(false) em bibliotecas chamadas com .Result/.Wait() voce deadlocka: a continuacao quer voltar ao contexto original, que esta bloqueado esperando o resultado. Em bibliotecas redistribuidas, sempre use ConfigureAwait(false) β€” voce nao sabe quem vai chamar.