Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Você pode evitar gargalos de desempenho e melhorar a capacidade de resposta geral do seu aplicativo usando programação assíncrona. No entanto, as técnicas tradicionais para escrever aplicações assíncronas podem ser complicadas, dificultando a escrita, depuração e manutenção.
C# oferece uma abordagem simplificada para programação assíncrona, tirando partido do suporte assíncrono no tempo de execução do .NET. O compilador faz o trabalho difícil que o desenvolvedor costumava fazer, e seu aplicativo mantém uma estrutura lógica que se assemelha ao código síncrono. Como resultado, você obtém todas as vantagens da programação assíncrona com uma fração do esforço.
Este tópico fornece uma visão geral de quando e como usar a programação assíncrona e inclui links para tópicos de suporte que contêm detalhes e exemplos.
O processamento assíncrono melhora a capacidade de resposta
A assincronia é essencial para atividades potencialmente bloqueadoras, como acesso à web. O acesso a um recurso da Web às vezes é lento ou atrasado. Se tal atividade for bloqueada em um processo síncrono, todo o aplicativo deverá aguardar. Em um processo assíncrono, o aplicativo pode continuar com outro trabalho que não depende do recurso da Web até que a tarefa potencialmente bloqueada seja concluída.
A tabela a seguir mostra áreas típicas onde a programação assíncrona melhora a capacidade de resposta. As APIs listadas do .NET e do Tempo de Execução do Windows contêm métodos que oferecem suporte à programação assíncrona.
Área de aplicação | Tipos .NET com métodos assíncronos | Tipos do Windows Runtime com métodos assíncronos |
---|---|---|
Acesso à Web | HttpClient | Windows.Web.Http.HttpClient SyndicationClient |
Trabalhar com ficheiros | JsonSerializer StreamReader StreamWriter XmlReader XmlWriter |
StorageFile |
Trabalhar com imagens | MediaCapture BitmapEncoder BitmapDecoder |
|
Programação WCF | Operações Síncronas e Assíncronas |
A assincronia se mostra especialmente valiosa para aplicativos que acessam o thread da interface do usuário, porque todas as atividades relacionadas à interface do usuário geralmente compartilham um thread. Se algum processo for bloqueado em um aplicativo síncrono, todos serão bloqueados. Seu aplicativo para de responder e você pode concluir que ele falhou quando, em vez disso, está apenas esperando.
Quando você usa métodos assíncronos, o aplicativo continua a responder à interface do usuário. Você pode redimensionar ou minimizar uma janela, por exemplo, ou pode fechar o aplicativo se não quiser esperar que ele termine.
A abordagem baseada em assincronia adiciona o equivalente a uma transmissão automática à lista de opções que você pode escolher ao projetar operações assíncronas. Ou seja, você obtém todos os benefícios da programação assíncrona tradicional, mas com muito menos esforço do desenvolvedor.
Os métodos assíncronos são fáceis de escrever
As palavras-chave async e await em C# são o coração da programação assíncrona. Usando essas duas palavras-chave, você pode usar recursos no .NET Framework, no .NET Core ou no Tempo de Execução do Windows para criar um método assíncrono quase tão facilmente quanto criar um método síncrono. Os métodos assíncronos que você define usando a palavra-chave async
são chamados de métodos assíncronos .
O exemplo a seguir mostra um método assíncrono. Quase tudo no código deve parecer familiar para você.
Você pode encontrar um exemplo completo do Windows Presentation Foundation (WPF) disponível para download em Programação assíncrona com async e await em C#.
public async Task<int> GetUrlContentLengthAsync()
{
using var client = new HttpClient();
Task<string> getStringTask =
client.GetStringAsync("https://learn.microsoft.com/dotnet");
DoIndependentWork();
string contents = await getStringTask;
return contents.Length;
}
void DoIndependentWork()
{
Console.WriteLine("Working...");
}
Você pode aprender várias práticas com o exemplo anterior. Comece com a assinatura do método. Inclui o modificador async
. O tipo de retorno é Task<int>
(Consulte a seção "Tipos de retorno" para obter mais opções). O nome do método termina em Async
. No corpo do método, GetStringAsync
retorna um Task<string>
. Isso significa que, ao fazeres a tarefa await
, receberás um string
(contents
). Antes de aguardar a tarefa, você pode fazer um trabalho que não dependa do string
de GetStringAsync
.
Preste muita atenção ao operador await
. Suspende GetUrlContentLengthAsync
:
-
GetUrlContentLengthAsync
não pode continuar até quegetStringTask
esteja concluído. - Enquanto isso, o controle retorna para o chamador de
GetUrlContentLengthAsync
. - O controle é retomado aqui quando
getStringTask
é concluído. - Em seguida, o operador
await
recupera o resultadostring
dogetStringTask
.
A instrução return especifica um resultado inteiro. Todos os métodos que estão aguardando GetUrlContentLengthAsync
recuperar o valor de comprimento.
Se GetUrlContentLengthAsync
não tiver nenhum trabalho que possa fazer entre chamar GetStringAsync
e aguardar a sua conclusão, podes simplificar o teu código chamando e aguardando numa única instrução.
string contents = await client.GetStringAsync("https://learn.microsoft.com/dotnet");
As características a seguir resumem o que torna o exemplo anterior um método assíncrono:
A assinatura do método inclui o modificador
async
.O nome de um método assíncrono, por convenção, termina com um sufixo "Async".
O tipo de retorno é um dos seguintes tipos:
-
Task<TResult> se o método tiver uma declaração return em que o operando é do tipo
TResult
. - Task se o seu método não tem nenhuma instrução return ou tem uma instrução return sem operando.
-
void
se você estiver escrevendo um manipulador de eventos assíncrono. - Qualquer outro tipo que tenha um método
GetAwaiter
.
Para obter mais informações, consulte a seção Tipos e parâmetros de retorno.
-
Task<TResult> se o método tiver uma declaração return em que o operando é do tipo
O método geralmente inclui pelo menos uma expressão
await
, que marca um ponto em que o método não pode continuar até que a operação assíncrona esperada seja concluída. Enquanto isso, o método é suspenso e o controle retorna ao chamador do método. A próxima seção deste tópico ilustra o que acontece no ponto de suspensão.
Em métodos assíncronos, você usa as palavras-chave e os tipos fornecidos para indicar o que deseja fazer, e o compilador faz o resto, incluindo acompanhar o que deve acontecer quando o controle retorna a um ponto de espera em um método suspenso. Alguns processos de rotina, como loops e tratamento de exceções, podem ser difíceis de lidar no código assíncrono tradicional. Em um método assíncrono, você escreve esses elementos da mesma forma que faria em uma solução síncrona e o problema é resolvido.
Para obter mais informações sobre assincronia em versões anteriores do .NET Framework, consulte TPL e programação assíncrona tradicional do .NET Framework.
O que acontece em um método assíncrono
A coisa mais importante a entender na programação assíncrona é como o fluxo de controle se move de método para método. O diagrama a seguir conduz você através do processo:
Os números no diagrama correspondem às etapas a seguir, iniciadas quando um método de chamada chama o método assíncrono.
Um método de chamada chama e aguarda o método assíncrono
GetUrlContentLengthAsync
.GetUrlContentLengthAsync
cria uma instância HttpClient e chama o método assíncrono GetStringAsync para baixar o conteúdo de um site como uma cadeia de caracteres.Algo acontece em
GetStringAsync
que suspende o seu progresso. Talvez deva esperar por um site para baixar ou alguma outra atividade de bloqueio. Para evitar o bloqueio de recursos,GetStringAsync
cede o controle ao chamador,GetUrlContentLengthAsync
.GetStringAsync
retorna um Task<TResult>, ondeTResult
é uma cadeia de caracteres eGetUrlContentLengthAsync
atribui a tarefa à variávelgetStringTask
. A tarefa representa o processo contínuo da chamada paraGetStringAsync
, com o compromisso de produzir um valor de string real quando o trabalho for concluído.Como
getStringTask
ainda não foi esperado,GetUrlContentLengthAsync
pode continuar com outras tarefas que não dependem do resultado final deGetStringAsync
. Esse trabalho é representado por uma chamada para o método síncronoDoIndependentWork
.DoIndependentWork
é um método síncrono que faz seu trabalho e retorna ao seu chamador.GetUrlContentLengthAsync
ficou sem trabalho que possa realizar sem obter um resultado degetStringTask
.GetUrlContentLengthAsync
próximo deseja calcular e retornar o comprimento da cadeia de caracteres baixada, mas o método não pode calcular esse valor até que o método tenha a cadeia de caracteres.Portanto,
GetUrlContentLengthAsync
usa um operador await para suspender seu progresso e entregar o controle ao método chamadoGetUrlContentLengthAsync
.GetUrlContentLengthAsync
retorna umTask<int>
para quem chamou. A tarefa representa uma promessa de produzir um resultado inteiro que é o comprimento da cadeia de caracteres baixada.Observação
Se
GetStringAsync
(e, portanto,getStringTask
) conclui antes queGetUrlContentLengthAsync
o espere, o controle permanece emGetUrlContentLengthAsync
. A despesa de suspender e depois retornar aoGetUrlContentLengthAsync
seria desperdiçada se o chamado processo assíncronogetStringTask
já tiver sido concluído eGetUrlContentLengthAsync
não tiver que esperar pelo resultado final.Dentro do método de chamada, o padrão de processamento continua. O chamador pode fazer outro trabalho que não depende do resultado do
GetUrlContentLengthAsync
antes de aguardar esse resultado, ou o chamador pode aguardar imediatamente. O método de chamada está aguardandoGetUrlContentLengthAsync
eGetUrlContentLengthAsync
está esperando porGetStringAsync
.GetStringAsync
completa e produz um resultado de cadeia de caracteres. O resultado da cadeia de caracteres não é retornado pela chamada paraGetStringAsync
da maneira esperada. (Lembre-se de que o método já retornou uma tarefa na etapa 3.) Em vez disso, o resultado da cadeia de caracteres é armazenado na tarefa que representa a conclusão do método,getStringTask
. O operador await recupera o resultado degetStringTask
. A declaração de atribuição atribui o resultado recuperado acontents
.Quando
GetUrlContentLengthAsync
tem o resultado da cadeia de caracteres, o método pode calcular o comprimento da cadeia de caracteres. Em seguida, o trabalho deGetUrlContentLengthAsync
também é concluído e o manipulador de eventos em espera pode retomar. No exemplo completo no final do tópico, você pode confirmar que o manipulador de eventos recupera e imprime o valor do resultado de comprimento. Se você é novo na programação assíncrona, reserve um minuto para considerar a diferença entre comportamento síncrono e assíncrono. Um método síncrono retorna quando seu trabalho é concluído (etapa 5), mas um método assíncrono retorna um valor de tarefa quando seu trabalho é suspenso (etapas 3 e 6). Quando o método assíncrono eventualmente conclui seu trabalho, a tarefa é marcada como concluída e o resultado, se houver, é armazenado na tarefa.
Métodos assíncronos de API
Você pode estar se perguntando onde encontrar métodos como GetStringAsync
que suportam programação assíncrona. O .NET Framework 4.5 ou superior e o .NET Core contêm muitos membros que trabalham com async
e await
. Você pode reconhecê-los pelo sufixo "Async" que é anexado ao nome do membro e pelo tipo de retorno de Task ou Task<TResult>. Por exemplo, a classe System.IO.Stream
contém métodos como CopyToAsync, ReadAsynce WriteAsync juntamente com os métodos síncronos CopyTo, Reade Write.
O Windows Runtime também contém muitos métodos que poderá usar com async
e await
em aplicações do Windows. Para obter mais informações, consulte de programação assíncrona e threading para desenvolvimento UWP e de programação assíncrona (aplicativos da Windows Store) e Guia de início rápido: chamando APIs assíncronas em C# ou Visual Basic se você usar versões anteriores do Tempo de Execução do Windows.
Tópicos
Os métodos assíncronos destinam-se a ser operações sem bloqueio. Uma expressão await
em um método assíncrono não bloqueia o thread atual enquanto a tarefa aguardada está em execução. Em vez disso, a expressão inscreve o restante do método como uma continuação e retorna o controle para o chamador do método assíncrono.
As palavras-chave async
e await
não fazem com que threads adicionais sejam criados. Os métodos assíncronos não exigem multithreading porque um método assíncrono não é executado em seu próprio thread. O método é executado no contexto de sincronização atual e usa o tempo no thread somente quando o método está ativo. Você pode usar Task.Run para mover o trabalho vinculado à CPU para um thread em segundo plano, mas um thread em segundo plano não ajuda com um processo que está apenas aguardando que os resultados fiquem disponíveis.
A abordagem baseada em assincronia para programação assíncrona é preferível às abordagens existentes em quase todos os casos. Particularmente, esta abordagem é melhor do que a classe BackgroundWorker para operações sujeitas a E/S porque o código é mais simples e você não precisa se proteger contra condições de concorrência. Em combinação com o método Task.Run, a programação assíncrona é melhor do que BackgroundWorker para operações vinculadas à CPU porque a programação assíncrona separa os detalhes de coordenação da execução do código do trabalho que Task.Run
transfere para o pool de threads.
assíncrono e aguardar
Se você especificar que um método é um método assíncrono usando o modificador de assíncrono, habilitará os dois recursos a seguir.
O método assíncrono marcado pode usar aguardar para designar pontos de suspensão. O operador
await
informa ao compilador que o método assíncrono não pode continuar após esse ponto até que o processo assíncrono aguardado seja concluído. Enquanto isso, o controle é restituído ao chamador do método assíncrono.A suspensão de um método assíncrono numa expressão
await
não constitui uma saída do método e os blocosfinally
não são executados.O método assíncrono marcado pode ser aguardado por métodos que o invocam.
Um método assíncrono normalmente contém uma ou mais ocorrências de um operador await
, mas a ausência de expressões await
não causa um erro de compilador. Se um método assíncrono não usar um operador await
para marcar um ponto de suspensão, o método será executado como um método síncrono, apesar do modificador async
. O compilador emite um aviso para tais métodos.
async
e await
são palavras-chave contextuais. Para obter mais informações e exemplos, consulte os seguintes tópicos:
Tipos e parâmetros de retorno
Um método assíncrono normalmente retorna um Task ou um Task<TResult>. Dentro de um método assíncrono, o operador await
é aplicado a uma tarefa retornada de uma chamada para outro método assíncrono.
Você especifica Task<TResult> como o tipo de retorno se o método contiver uma instrução return
que especifica um operando do tipo TResult
.
Você usa Task como o tipo de retorno se o método não tiver nenhuma instrução de retorno ou caso tenha uma instrução de retorno que não retorne um operando.
Você também pode especificar qualquer outro tipo de retorno, desde que o tipo inclua um método GetAwaiter
.
ValueTask<TResult> é um exemplo desse tipo. Ele está disponível no pacote System.Threading.Tasks.Extension NuGet.
O exemplo a seguir mostra como você declara e chama um método que retorna um Task<TResult> ou um Task:
async Task<int> GetTaskOfTResultAsync()
{
int hours = 0;
await Task.Delay(0);
return hours;
}
Task<int> returnedTaskTResult = GetTaskOfTResultAsync();
int intResult = await returnedTaskTResult;
// Single line
// int intResult = await GetTaskOfTResultAsync();
async Task GetTaskAsync()
{
await Task.Delay(0);
// No return statement needed
}
Task returnedTask = GetTaskAsync();
await returnedTask;
// Single line
await GetTaskAsync();
Cada tarefa retornada representa o trabalho contínuo. Uma tarefa encapsula informações sobre o estado do processo assíncrono e, eventualmente, o resultado final do processo ou a exceção que o processo gera se não for bem-sucedido.
Um método assíncrono também pode ter um tipo de retorno void
. Esse tipo de retorno é usado principalmente para definir manipuladores de eventos, onde um tipo de retorno void
é necessário. Os manipuladores de eventos assíncronos geralmente servem como ponto de partida para programas assíncronos.
Um método assíncrono que tem um tipo de retorno void
não pode ser aguardado, e o chamador de um método que retorna void não pode apanhar qualquer exceção que o método lance.
Um método assíncrono não pode declarar em, ref ou out parâmetros, mas o método pode chamar métodos que tenham esses parâmetros. Da mesma forma, um método assíncrono não pode retornar um valor por referência, embora possa chamar métodos com valores de retorno ref.
Para obter mais informações e exemplos, consulte Async return types (C#).
As APIs assíncronas na programação do Tempo de Execução do Windows têm um dos seguintes tipos de retorno, que são semelhantes às tarefas:
- IAsyncOperation<TResult>, que corresponde a Task<TResult>
- IAsyncAction, que corresponde a Task
- IAsyncActionWithProgress<TProgress>
- IAsyncOperationWithProgress<TResult,TProgress>
Convenção de nomenclatura
Por convenção, os métodos que retornam tipos comumente esperados (por exemplo, Task
, Task<T>
, ValueTask
, ValueTask<T>
) devem ter nomes que terminem com "Async". Os métodos que iniciam uma operação assíncrona, mas não retornam um tipo aguardado, não devem ter nomes que terminem com "Async", mas podem começar com "Begin", "Start" ou algum outro verbo para sugerir que esse método não retorna ou lança o resultado da operação.
Você pode ignorar a convenção em que um evento, classe base ou contrato de interface sugere um nome diferente. Por exemplo, você não deve renomear manipuladores de eventos comuns, como OnButtonClick
.
Artigos relacionados (Visual Studio)
Título | Descrição |
---|---|
Como fazer várias solicitações da Web em paralelo usando async e await (C#) | Demonstra como iniciar várias tarefas ao mesmo tempo. |
Tipos de retorno assíncrono (C#) | Ilustra os tipos que os métodos assíncronos podem retornar e explica quando cada tipo é apropriado. |
Cancele tarefas com um token de cancelamento como mecanismo de sinalização. | Mostra como adicionar a seguinte funcionalidade à sua solução assíncrona: - Cancelar uma lista de tarefas (C#) - Cancelar tarefas após um período de tempo (C#) - Processar tarefas assíncronas à medida que são concluídas (C#) |
Usando assíncrono para acesso a ficheiros (C#) | Lista e demonstra os benefícios de usar async e await para acessar arquivos. |
Padrão assíncrono baseado em tarefas (TAP) | Descreve um padrão assíncrono, o padrão é baseado nos tipos Task e Task<TResult>. |
Vídeos assíncronos no Channel 9 | Fornece links para uma variedade de vídeos sobre programação assíncrona. |