C#
- Boas práticas para usar com async e await
![]() |
Hoje veremos boas práticas associadas com o uso da programação assíncrona usando async e await. |
No mundo do desenvolvimento de software moderno, escrever código assíncrono é essencial para garantir que seus aplicativos sejam responsivos e escaláveis.
A plataforma .NET Core fornece ferramentas poderosas para gerenciar operações assíncronas usando as palavras-chave async e await, e , veremos práticas recomendadas para usar async/await em C# com .NET Core, começando com exemplos simples e avançando gradualmente para cenários mais complexos.
Por que usar Async/Await?
Antes de mergulhar nas práticas recomendadas, vamos entender brevemente por que
usamos async/await em C#:
- Ter Interfaces de usuário responsivas: O código assíncrono permite que
a interface de usuário do seu aplicativo permaneça responsiva, mesmo ao executar
operações demoradas, como consultas de banco de dados ou solicitações da web.
- Obter Escalabilidade aprimorada: As operações assíncronas permitem lidar com eficiência com um grande número de tarefas simultâneas, tornando seu aplicativo mais escalonável.
- Garantir eficiência de recursos: Ao evitar o bloqueio de chamadas, o código assíncrono permite que threads sejam reutilizados de forma eficiente, reduzindo o consumo de recursos.
- Melhorar experiência do usuário: A execução mais rápida de tarefas de longa duração resulta em uma melhor experiência geral do usuário.
Boas práticas para async e await
1- Use async sempre que for possível e pertinente
Sempre que você encontrar operações vinculadas a operações E/S, como consultas de banco de dados, solicitações de rede ou E/S de arquivo, considere tornar o método assíncrono.
Por exemplo:
1- Obter dados do site jsonplaceholder
Console.WriteLine("Pressione
algo para obter os dados "); Console.ReadKey(); await FazerRequisicaoAsync();Console .ReadLine();static async Task FazerRequisicaoAsync(){ // Crie uma instância do HttpClient using (var httpClient = new HttpClient()) { try { // Especifique a URL que você deseja acessar. string url = "https://jsonplaceholder.typicode.com/posts/1"; // Faça uma solicitação GET de forma assíncrona e aguarde a resposta. HttpResponseMessage response = await httpClient.GetAsync(url); // Verifique se a resposta foi bem-sucedida (código de status 200 OK). if (response.IsSuccessStatusCode) { // Leia o conteúdo da resposta de forma assíncrona. string conteudo = await response.Content.ReadAsStringAsync(); // Exiba o conteúdo da página. Console.WriteLine(conteudo); } else { Console.WriteLine("A solicitação não foi bem-sucedida. Código de status: " + response.StatusCode); } } catch (HttpRequestException e) { Console.WriteLine("Erro na solicitação HTTP: " + e.Message); } } } |
- Neste exemplo,
criamos o método
FazerRequisicaoAsync()
como um método
assíncrono que simula uma solicitação HTTP usando HttpClient.
- Usamos await para aguardar de forma assíncrona a
leitura da resposta HTTP e do conteúdo.
- Chamamos
FazerRequisicaoAsync()
de forma assíncrona, imprimindo o resultado no console.
Aqui está outro exemplo simples de como um método assíncrono pode ser útil em uma operação de E/S:
public
async
Task<string>
LerArquivoAsync(string
caminhoArquivo) { using (var leitor = new StreamReader(caminhoArquivo)) { return await leitor.ReadToEndAsync(); } } |
2- Use ConfigureAwait(false) para threads que não tratam com UI
o método ConfigureAwait(false) é uma opção que você pode usar para controlar o contexto de sincronização no qual o código assíncrono é executado. Isso é útil quando você deseja evitar que o código continue em um contexto específico após uma operação assíncrona ser concluída, como um contexto de thread específico (por exemplo, o contexto da thread principal da interface do usuário).
Quando você utiliza o await em uma operação assíncrona, o código após o await pode continuar a ser executado no mesmo contexto de sincronização do qual a operação assíncrona estava sendo chamada. Isso é importante em cenários nos quais você deseja manter o contexto atual, como a UI thread em aplicativos Windows Forms ou WPF, onde as atualizações de interface do usuário devem ser feitas no contexto da thread principal.
No entanto, em algumas situações, você pode querer evitar o contexto de sincronização e permitir que o código assíncrono continue em qualquer contexto disponível.
É aí que o ConfigureAwait(false) entra em jogo.
Quando você utiliza ConfigureAwait(false), você está dizendo ao runtime para não tentar continuar a execução no contexto original de sincronização após a conclusão da operação assíncrona.
processá-la.
public
async
Task
MeuMetodoAsync() { // O código antes do await é executado no contexto atual. await MinhaOperacao().ConfigureAwait(false); // O código após o await não será executado no contexto original de sincronização. } |
Neste exemplo, o código após o await com ConfigureAwait(false) não será executado no contexto original de sincronização, o que pode ser útil em cenários onde você deseja evitar bloqueios ou quando você está escrevendo código em bibliotecas que não deveriam assumir nada sobre o contexto de sincronização.
É importante observar que o uso de ConfigureAwait(false) pode melhorar o desempenho em cenários de I/O, onde o contexto de sincronização pode ser desnecessário e causar overhead. No entanto, ao usar ConfigureAwait(false), você deve ter cuidado para não causar problemas em cenários de interface do usuário, onde a atualização da UI deve ser feita no contexto da thread principal.
Certifique-se de usá-lo com discernimento e compreensão das implicações do contexto de sincronização em seu código.
3- Evite usar async void
Você deve evitar o uso de async void na maioria dos cenários em C# porque ele tem implicações significativas e pode levar a problemas de gerenciamento de exceções e controle de fluxo.
Aqui estão algumas razões para evitar o uso de async void:
- Dificuldades no tratamento de exceções: Quando um método async retorna void, você perde a capacidade de aguardar a conclusão da operação assíncrona usando await.
- Falta de feedback sobre a conclusão: Métodos async que retornam Task ou Task<T> permitem que você saiba quando a operação assíncrona foi concluída;
- Dificuldades de teste: Quando um método é async void, pode ser complicado testá-lo, pois não há um mecanismo direto para aguardar a conclusão da operação. Isso pode dificultar a criação de testes unitários robustos.
- Comportamento imprevisível em aplicativos de interface do usuário: Em aplicativos de interface do usuário (como WPF ou Windows Forms), o uso de async void pode levar a problemas, como bloqueio da interface do usuário ou falhas não tratadas, quando exceções são lançadas
Exemplo:
|
Entendendo o código:
Definimos um evento chamado TarefaConcluida usando o delegado EventHandler<string>. Este evento será gerado quando a operação assíncrona for concluída e carrega uma mensagem como parâmetro de string.
Aqui mostramos o
uso de async void para o manipuladores
de eventos assíncronos que é um cenário onde ele pode ser usado.
No método
RealizarOperacaoAsync,
realizamos uma operação assíncrona com atraso para simular o trabalho que está
sendo realizado. Após a conclusão da operação, invocamos o evento
TarefaConcluida,
passando a mensagem “Tarefa concluída”.
O método
TratarEventoAsyncpermanece
inalterado e continua a ser um manipulador de eventos assíncronos.
No método Main, criamos uma instância de
ExemploAsync
e também assinamos o evento
TarefaConcluidausando
uma expressão lambda. Quando o evento é gerado, a expressão lambda imprime a
mensagem recebida no console.
4- Use os métodos Task.WhenAll ou Task.WhenAny para execução em paralelo
Os métodos Task.WhenAll e Task.WhenAny são métodos estáticos disponíveis em C# para trabalhar com tarefas assíncronas em paralelo, permitindo que você aguarde várias tarefas de forma eficiente.
Exemplo:
|
Entendendo o código:
Neste exemplo, temos três métodos assíncronos: ObtemDadosAsync, ProcessaDadosAsync e SalvaDadosAsync onde cada método simula uma operação com um atraso específico.
O método
ProcessaDadosConcorrenteAsync
compõe esses métodos
assíncronos em uma lista de tarefas, cada uma retornando um resultado de string.
Usamos List<Task<string>> para armazenar as tarefas
e seus resultados.
O método Task.WhenAll é usado para iniciar todas as
tarefas simultaneamente e aguardar a conclusão de todas elas. Isso nos permite
realizar múltiplas operações assíncronas simultaneamente, melhorando o
desempenho geral.
Depois que todas as tarefas são concluídas, os resultados são coletados em uma
série de strings, results. Você pode processar esses resultados ou realizar
quaisquer outras operações necessárias. Neste exemplo, simplesmente imprimimos
cada resultado no console.
Este código demonstra o poder de compor operações assíncronas usando Task.WhenAll.
Ele permite que você execute várias tarefas
assíncronas simultaneamente e espere com eficiência que todas elas sejam
concluídas. Isso é especialmente útil em cenários onde você precisa executar
diversas operações assíncronas em paralelo, como buscar dados de diferentes
fontes, processá-los e salvá-los simultaneamente.
Esses são alguns cenários onde temos o uso da programação assíncrona e onde podemos aplicar estas boas práticas.
E estamos conversados...
"Finalmente apareceu (Jesus) aos onze, estando eles
assentados à mesa, e lançou-lhes em rosto a sua incredulidade e dureza de
coração, por não haverem crido nos que o tinham visto já ressuscitado."
Marcos 16:14
Referências:
.NET MAUI - Lançamento da Release Candidate