Introdução à programação assíncrona em C#
Programação assíncrona é uma paradigma fundamental no desenvolvimento moderno em C# (.NET), permitindo criar aplicações responsivas e de alto desempenho. Diferentemente da abordagem síncrona, onde a execução do código bloqueia a thread atual até a conclusão da operação, o código assíncrono libera a thread para executar outras tarefas enquanto aguarda o resultado de uma operação longa (por exemplo, consulta ao banco de dados, chamada HTTP ou leitura de arquivo).
Com o surgimento das palavras-chave async e await no C# 5.0, a programação assíncrona deixou de ser complexa e tornou-se acessível a todos os desenvolvedores. Neste guia, abordaremos todos os conceitos-chave: desde os fundamentos de Task e ValueTask até o tratamento de erros e cancelamento de operações.
Fundamentos de async/await e Task
O que são Task e Task<TResult>
Task é o tipo central na TPL (Task Parallel Library), representando uma operação assíncrona. Task (sem valor de retorno) e Task<TResult> (com valor de retorno) são os blocos de construção do código assíncrono.
// Método assíncrono simples sem valor de retornopublic async Task DoSomethingAsync(){ await Task.Delay(1000); Console.WriteLine("Pronto!");}
// Método assíncrono com valor de retornopublic async Task<int> CalculateSumAsync(int a, int b){ await Task.Delay(500); // Simulação de operação longa return a + b;}Palavras-chave async e await
O modificador async indica ao compilador que o método contém código assíncrono. O operador await pausa a execução do método até a conclusão da tarefa aguardada, sem bloquear a thread.
public async Task ProcessDataAsync(){ Console.WriteLine("Início do processamento..."); // Espera assíncrona — a thread não é bloqueada string result = await FetchDataFromApiAsync(); Console.WriteLine($"Resultado obtido: {result}");}Regras de uso do async/await
- Não use async void (exceto em manipuladores de eventos) — isso torna impossível rastrear exceções.
- Evite chamadas bloqueantes (
.Result,.Wait()) — elas podem causar deadlocks. - Nomeie métodos assíncronos com o sufixo "Async" para clareza.
Técnicas avançadas: ValueTask, CancellationToken e paralelismo
ValueTask vs Task
ValueTask é uma otimização para casos onde a operação assíncrona frequentemente é concluída de forma síncrona (por exemplo, dados em cache). Diferentemente de Task, que é um tipo por referência, ValueTask é uma struct, o que reduz a carga no garbage collector.
public ValueTask<int> GetCachedOrFetchAsync(int id){ if (cache.ContainsKey(id)) { // Conclusão síncrona — sem alocação de memória return new ValueTask<int>(cache[id]); } // Conclusão assíncrona return new ValueTask<int>(FetchFromDatabaseAsync(id));}Cancelamento de operações com CancellationToken
CancellationToken permite interromper corretamente operações assíncronas longas. Sempre passe o token de cancelamento se o método o aceitar.
public async Task<string> DownloadFileAsync(string url, CancellationToken ct){ using var client = new HttpClient(); var response = await client.GetAsync(url, ct); // ct é passado para a API return await response.Content.ReadAsStringAsync();}
// Usovar cts = new CancellationTokenSource();cts.CancelAfter(TimeSpan.FromSeconds(5)); // Timeout de 5 segundos
try{ string data = await DownloadFileAsync("https://example.com", cts.Tok