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.
No .NET, o padrão assíncrono baseado em tarefas é o padrão de design assíncrono recomendado para novos desenvolvimentos. Baseia-se nos tipos Task e Task<TResult> no espaço de nomes System.Threading.Tasks, que representam operações assíncronas.
Nomenclatura, parâmetros e tipos de retorno
A TAP usa um único método para representar o início e a conclusão de uma operação assíncrona. Esta abordagem contrasta tanto com o Modelo de Programação Assíncrona (APM ou IAsyncResult) como com o Padrão Assíncrono Baseado em Eventos (EAP). APM requer Begin e End métodos. O EAP requer um método que tenha o sufixo Async e também requer um ou mais eventos, tipos delegados do manipulador de eventos e os tipos derivados de EventArg. Os métodos assíncronos em TAP incluem o sufixo Async após o nome da operação para métodos que retornam tipos aguardados, como Task, Task<TResult>, ValueTaske ValueTask<TResult>. Por exemplo, uma operação assíncrona Get que retorna um Task<String> pode ser nomeada GetAsync. Se você estiver adicionando um método TAP a uma classe que já contém um nome de método EAP com o sufixo Async , use o sufixo TaskAsync . Por exemplo, se a classe já tiver um GetAsync método, use o nome GetTaskAsync. Se um método inicia uma operação assíncrona, mas não retorna um tipo aguardado, seu nome deve 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.
Um método TAP retorna System.Threading.Tasks.Task ou System.Threading.Tasks.Task<TResult>, com base em se o método síncrono correspondente retorna void ou um tipo TResult.
Os parâmetros de um método TAP devem corresponder aos parâmetros do seu homólogo síncrono e devem ser fornecidos pela mesma ordem. No entanto, out e ref os parâmetros estão isentos desta regra e devem ser totalmente evitados. Qualquer dado que um parâmetro out ou ref devolve deve, pelo contrário, integrar o TResult devolvido por Task<TResult>, e deve usar um tuplo ou uma estrutura de dados personalizada para acomodar múltiplos valores. Além disso, considere adicionar um CancellationToken parâmetro mesmo que a contraparte síncrona do método TAP não ofereça um.
Métodos dedicados exclusivamente à criação, manipulação ou combinação de tarefas (onde a intenção assíncrona do método é clara no nome do método ou no nome do tipo a que pertence) não precisam de seguir este padrão de nomenclatura. Estes métodos são frequentemente designados por combinadores. Exemplos de combinadores incluem WhenAll e WhenAny, e são discutidos na seção Usando os combinadores baseados em tarefas internas do artigo Consumindo o padrão assíncrono baseado em tarefas.
Para obter exemplos de como a sintaxe TAP difere da sintaxe usada em padrões de programação assíncrona herdados, como o Modelo de Programação Assíncrona (APM) e o Padrão Assíncrono Baseado em Evento (EAP), consulte Padrões de programação assíncrona.
Comportamento assíncrono, tipos de retorno e nomeação
A async palavra-chave não força um método a correr assíncronamente noutro thread. Permite await, e o método é executado de forma síncrona até atingir um objeto aguardável incompleto. Se o método não alcançar um elemento aguardável incompleto, pode ser concluído de forma síncrona.
Para a maioria das APIs, prefira estes tipos de retorno:
- Task Use para operações assíncronas que não produzam um valor.
- Use Task<TResult> para operações assíncronas que produzam um valor.
- Usar ValueTask ou ValueTask<TResult> apenas quando as medições mostrarem pressão de alocação e quando os consumidores conseguirem lidar com as restrições adicionais de uso.
Mantenha a designação do TAP previsível.
- Use os
Asyncsufixos para métodos que retornam tipos esperáveis. - Não adiciones
Asyncaos métodos síncronos. - Adicione a nova
MethodNameAsyncsobrecarga juntamente com a implementação existente `MethodName`. Não removas nem renomees a API síncrona. Manter ambos permite que os chamadores migrem ao seu próprio ritmo sem alterações bruscas.
Iniciando uma operação assíncrona
Um método assíncrono baseado em TAP pode fazer uma pequena quantidade de trabalho de forma síncrona, como validar argumentos e iniciar a operação assíncrona, antes de retornar a tarefa resultante. Mantenha o trabalho síncrono ao mínimo para que o método assíncrono possa regressar rapidamente. As razões para um retorno rápido incluem:
- Podes invocar métodos assíncronos a partir de threads de interface de utilizador (UI), e qualquer trabalho síncrono prolongado pode prejudicar a capacidade de resposta da aplicação.
- Podes lançar vários métodos assíncronos em simultâneo. Portanto, qualquer trabalho de longa execução na parte síncrona de um método assíncrono pode atrasar o início de outras operações assíncronas, diminuindo assim os benefícios da simultaneidade.
Em alguns casos, a quantidade de trabalho necessária para concluir a operação é menor do que a quantidade de trabalho necessária para iniciar a operação de forma assíncrona. Ler de um fluxo onde a operação de leitura pode ser satisfeita por dados já armazenados em memória é um exemplo desse cenário. Nesses casos, a operação pode ser concluída de forma síncrona e pode devolver uma tarefa já concluída.
Exceções
Um método assíncrono deve lançar uma exceção diretamente da chamada do método assíncrono apenas em resposta a um erro de utilização. Erros de uso nunca devem ocorrer no código de produção. Por exemplo, se passar uma referência nula (Nothing no Visual Basic) como um dos argumentos do método causa um estado de erro (geralmente representado por uma ArgumentNullException exceção), você pode modificar o código de chamada para garantir que uma referência nula nunca seja passada. Para todos os outros erros, atribua exceções que ocorrem quando um método assíncrono está a ser executado para a tarefa devolvida, mesmo que o método assíncrono se complete síncronamente antes da tarefa ser retornada. Normalmente, uma tarefa contém no máximo uma exceção. No entanto, se a tarefa representar múltiplas operações (por exemplo, WhenAll), múltiplas exceções podem estar associadas a uma única tarefa.
Ambiente de destino
Ao implementar um método TAP, você pode determinar onde a execução assíncrona ocorre. Pode optar por executar a carga de trabalho no pool de threads, implementá-la usando I/O assíncrona (sem estar vinculada a uma thread durante a maior parte da execução da operação), executá-la numa thread específica (como a thread da interface), ou usar qualquer número de contextos potenciais. Um método TAP pode até não ter nada para executar, e pode apenas devolver a Task que representa a ocorrência de uma condição noutro local do sistema (por exemplo, uma tarefa que representa dados a chegar a uma estrutura de dados em fila).
O chamador do método TAP pode bloquear a espera que o método TAP seja concluído, aguardando sincronizadamente a tarefa resultante, ou pode executar código adicional (de continuação) quando a operação assíncrona terminar. O criador do código de continuação tem controle sobre onde esse código é executado. Pode criar o código de continuação explicitamente, através de métodos na classe Task (por exemplo, ContinueWith), ou implicitamente, usando suporte de linguagem construído sobre continuações (por exemplo, await em C#, Await em Visual Basic, AwaitValue em F#).
Estado da tarefa
A Task classe fornece um ciclo de vida para operações assíncronas, e esse ciclo é representado pela enumeração TaskStatus. Para suportar casos extremos de tipos que derivam de Task e Task<TResult>, e para apoiar a separação da construção do agendamento, a classe Task expõe um método Start. As tarefas criadas pelos construtores públicos Task são chamadas de tarefas frias, porque começam seu ciclo de vida no estado não agendado Created e são agendadas somente quando Start são chamadas nessas instâncias.
Todas as outras tarefas começam o seu ciclo de vida num estado quente, o que significa que as operações assíncronas que representam já estão iniciadas e o seu estado de tarefa é um valor de enumeração diferente de TaskStatus.Created. Todas as tarefas retornadas dos métodos TAP devem ser ativadas. Se um método TAP usa internamente o construtor de uma tarefa para instanciar a tarefa a ser retornada, o método TAP deve chamar Start o Task objeto antes de retorná-lo. Os consumidores de um método TAP podem assumir com segurança que a tarefa devolvida está em execução e não devem tentar chamar o método Start em qualquer Task que seja devolvido de um método TAP. Chamar Start em uma tarefa ativa resulta numa InvalidOperationException excepção.
Para orientações sobre preocupações de ciclo de vida e gestão de execução sem acompanhamento após a ativação da tarefa, consulte Manter os métodos assíncronos vivos.
Cancelamento (opcional)
Na TAP, o cancelamento é opcional para implementadores de método assíncrono e consumidores de método assíncrono. Se uma operação permitir o cancelamento, ela expõe uma sobrecarga do método assíncrono que aceita um token de cancelamento (CancellationToken instância). Por convenção, o parâmetro é chamado cancellationToken.
public static Task ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
Public Function ReadAsync(buffer As Byte(), offset As Integer, count As Integer,
cancellationToken As CancellationToken) As Task
A operação assíncrona monitora esse token para solicitações de cancelamento. Se receber um pedido de cancelamento, pode optar por aceder esse pedido e cancelar a operação. Se o pedido de cancelamento resultar numa conclusão prematura do trabalho, o método TAP devolve uma tarefa que termina nesse Canceled estado; não há resultado disponível nem é lançada nenhuma exceção. O Canceled estado é considerado um estado final (concluído) para uma tarefa, juntamente com os Faulted estados e RanToCompletion . Portanto, se uma tarefa estiver no Canceled estado, sua IsCompleted propriedade retornará true. Quando uma tarefa é concluída no estado Canceled, todas as continuações registadas com a tarefa são agendadas ou executadas, a menos que uma opção de continuação, como a NotOnCanceled, tenha sido especificada para não continuar. Qualquer código que esteja aguardando assincronamente por uma tarefa cancelada por meio do uso de recursos de linguagem continua a ser executado, mas recebe uma OperationCanceledException ou uma exceção derivada dele. Código que é bloqueado, aguardando de forma síncrona na tarefa através de métodos como Wait e WaitAll, continua a ser executado com uma exceção.
Se um token de cancelamento solicitar cancelamento antes de o método TAP que aceita esse token ser chamado, o método TAP deve devolver uma tarefa Canceled. No entanto, se o cancelamento for solicitado enquanto a operação assíncrona estiver em execução, a operação assíncrona não precisará aceitar a solicitação de cancelamento. A tarefa retornada deve terminar no Canceled estado somente se a operação terminar como resultado da solicitação de cancelamento. Se o cancelamento for solicitado, mas ainda for produzido um resultado ou uma exceção, a tarefa deve terminar no estado RanToCompletion ou Faulted.
Para métodos assíncronos que desejam oferecer a possibilidade de serem cancelados prioritariamente, não é necessário dar uma sobrecarga que não aceite um token de cancelamento. Para métodos que não podem ser cancelados, não forneça sobrecargas que aceitem um token de cancelamento; Isso ajuda a indicar ao chamador se o método de destino é realmente cancelável. Um código de consumo que não pretende cancelamento pode chamar um método que aceite um CancellationToken e fornecer None como valor do argumento. None é funcionalmente equivalente ao padrão CancellationToken.
Relatório de progresso (opcional)
Algumas operações assíncronas beneficiam de fornecer notificações de progresso. Normalmente, utiliza-se estas notificações para atualizar uma interface de utilizador com informações sobre o progresso da operação assíncrona.
No TAP, gere o progresso através de uma IProgress<T> interface. Passar esta interface para o método assíncrono como um parâmetro, normalmente chamado progress. Quando fornece a interface de progresso no momento de chamar o método assíncrono, ajuda a eliminar as condições de concorrência resultantes de um uso incorreto. Estas condições de corrida ocorrem quando os manipuladores de eventos são registados de forma incorreta após o início da operação e perdem atualizações. Mais importante ainda, a interface de progresso suporta implementações variadas de progresso, conforme determinado pelo código consumidor. Por exemplo, o código que consome pode interessar-se apenas pela atualização de progresso mais recente ou pode querer armazenar todas as atualizações, invocar uma ação para cada atualização, ou controlar se a invocação é transmitida para um determinado thread de execução. Todas estas opções são possíveis através de diferentes implementações da interface, personalizadas às necessidades específicas do consumidor. Tal como acontece com o cancelamento, as implementações TAP devem fornecer um IProgress<T> parâmetro apenas se a API suportar notificações de progresso.
Por exemplo, se o ReadAsync método discutido anteriormente neste artigo pode reportar progresso intermédio sob a forma do número de bytes lidos até agora, o callback de progresso pode ser uma IProgress<T> interface:
public static Task ReadAsync(byte[] buffer, int offset, int count,
IProgress<long> progress)
Public Function ReadAsync(buffer As Byte(), offset As Integer, count As Integer,
progress As IProgress(Of Long)) As Task
Se um FindFilesAsync método devolver uma lista de todos os arquivos que atendem a um padrão de pesquisa específico, o callback de progresso poderá fornecer uma estimativa da porcentagem de trabalho concluído e o conjunto atual de resultados parciais. Poderia fornecer esta informação com uma tupla, por exemplo:
public static Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<Tuple<double, ReadOnlyCollection<List<FileInfo>>>> progress)
Public Function FindFilesAsync(
pattern As String,
progress As IProgress(Of Tuple(Of Double, ReadOnlyCollection(Of List(Of FileInfo))))) As Task(Of ReadOnlyCollection(Of FileInfo))
ou com um tipo de dados específico da API:
public static Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<FindFilesProgressInfo> progress)
Public Function FindFilesAsync(
pattern As String,
progress As IProgress(Of FindFilesProgressInfo)) As Task(Of ReadOnlyCollection(Of FileInfo))
Neste último caso, o tipo de dados especial é geralmente sufixado com ProgressInfo.
Se as implementações TAP fornecerem sobrecargas que aceitam um progress parâmetro, devem permitir que o argumento seja null. Se passares null, não é reportado progresso. As implementações TAP devem relatar o progresso para o objeto Progress<T> de forma síncrona, o que permite que o método assíncrono forneça progresso rapidamente. Também permite que o consumidor do progresso determine como e onde melhor lidar com as informações. Por exemplo, a instância de progresso poderá optar por organizar chamadas de retorno e gerar eventos num contexto de sincronização capturado.
Implementações IProgress<T>
O .NET fornece a classe Progress<T>, que implementa IProgress<T>. A classe Progress<T> é declarada da seguinte forma:
public class Progress<T> : IProgress<T>
{
public Progress();
public Progress(Action<T> handler);
protected virtual void OnReport(T value);
public event EventHandler<T>? ProgressChanged;
}
Uma instância de Progress<T> expõe um evento de ProgressChanged, que é acionado sempre que a operação assíncrona relata uma atualização de progresso. O evento ProgressChanged é ativado no objeto SynchronizationContext que a instância Progress<T> captura ao ser instanciada. Se não houver contexto de sincronização disponível, utiliza-se um contexto predefinido direcionado ao pool de threads. Pode registar os manipuladores neste evento. Para conveniência, pode também fornecer um único handler ao Progress<T> construtor. Este handler comporta-se exatamente como um gestor de eventos para o ProgressChanged evento. As atualizações de progresso são geradas de forma assíncrona para evitar atrasar a operação assíncrona enquanto os manipuladores de eventos estão em execução. Outra IProgress<T> implementação poderia optar por aplicar semânticas diferentes.
Escolha das sobrecargas a serem fornecidas
Se uma implementação TAP usar os parâmetros opcionais CancellationToken e opcionais IProgress<T> , ela poderá exigir até quatro sobrecargas:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
public Task MethodNameAsync(…, IProgress<T> progress);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken cancellationToken) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
No entanto, muitas implementações TAP não fornecem recursos de cancelamento ou progresso, por isso exigem um único método:
public Task MethodNameAsync(…);
Public MethodNameAsync(…) As Task
Se uma implementação TAP suportar cancelamento ou progresso, mas não ambos, pode fornecer duas sobrecargas:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
// … or …
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken) As Task
' … or …
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Se uma implementação de TAP suportar tanto o cancelamento quanto o progresso, poderá expor todas as quatro sobrecargas. No entanto, pode fornecer apenas os seguintes dois:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
Para compensar as duas combinações intermédias em falta, os programadores podem passar None ou aplicar um padrão CancellationToken para o cancellationToken parâmetro e null para o progress parâmetro.
Se esperares que todas as utilizações do método TAP suportem cancelamento ou progresso, podes omitir as sobrecargas que não aceitam o parâmetro relevante.
Se decidires expor múltiplas sobrecargas para tornar o cancelamento ou o progresso opcionais, as sobrecargas que não suportam cancelamento ou progresso devem funcionar como se passassem None para cancelamento ou null para progresso à sobrecarga que suporta esses parâmetros.
Artigos relacionados
- Padrões de Programação Assíncrona — Introduz os três padrões para realizar operações assíncronas: o Padrão Assíncrono Baseado em Tarefas (TAP), o Modelo de Programação Assíncrona (APM) e o Padrão Assíncrono Baseado em Eventos (EAP).
- Implementing the Task-based Asynchronous Pattern — Descreve como implementar o TAP de três formas: usando os compiladores C# e Visual Basic em Visual Studio, manualmente, ou através de uma combinação dos métodos compilador e manual.
- Consumir o Padrão Assíncrono Baseado em Tarefas — Descreve como pode-se usar tarefas e retornos de chamada para aguardar sem bloquear.
- Interoperar com Outros Padrões e Tipos Assíncronos — Descreve como usar o TAP para implementar o Modelo de Programação Assíncrona (APM) e o Padrão Assíncrono Baseado em Eventos (EAP).