Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Em .NET, o padrão assíncrono baseado em tarefa é o padrão de design assíncrono recomendado para o novo desenvolvimento. Ele é baseado nos tipos Task e Task<TResult> no System.Threading.Tasks namespace, que representam operações assíncronas.
Nomenclatura, parâmetros e tipos de retorno
O TAP usa um único método para representar a iniciação e a conclusão de uma operação assíncrona. Essa abordagem contrasta com o padrão de Modelo de Programação Assíncrono (APM ou IAsyncResult) e o Padrão Assíncrono baseado em Evento (EAP). O 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 de manipulador de eventos e tipos derivados de EventArg. Os métodos assíncronos no TAP incluem o Async sufixo 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 iniciar uma operação assíncrona, mas não retornar um tipo aguardável, seu nome deverá começar com Begin, Start ou algum outro verbo para sugerir que esse método não retorne ou lance o resultado da operação.
Um método TAP retorna um System.Threading.Tasks.Task ou um System.Threading.Tasks.Task<TResult>, com base em se o método síncrono correspondente retorna nulo ou um tipo TResult.
Os parâmetros de um método TAP devem corresponder aos parâmetros de seu equivalente síncrono e devem ser fornecidos na mesma ordem. No entanto, out e ref os parâmetros são isentos dessa regra e devem ser totalmente evitados. Todos os dados que um parâmetro out ou ref retorna devem, em vez disso, fazer parte do TResult retornado por Task<TResult> e devem usar uma tupla ou uma estrutura de dados personalizada para acomodar múltiplos valores. Além disso, considere adicionar um CancellationToken parâmetro mesmo que o equivalente síncrono do método TAP não ofereça um.
Os métodos dedicados exclusivamente à criação, manipulação ou combinação de tarefas (em que a intenção assíncrona do método é clara no nome do método ou no nome do tipo ao qual o método pertence) não precisam seguir esse padrão de nomenclatura. Esses métodos geralmente são chamados de combinadores. Exemplos de combinadores incluem WhenAll e WhenAny, e são discutidos na seção Usando os combinadores internos baseados em tarefas 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íncrono.
Comportamento assíncrono, tipos de retorno e nomenclatura
A async palavra-chave não força um método a ser executado de forma assíncrona em outro thread. Ele habilita await, e o método é executado de forma síncrona até atingir uma expressão aguardável incompleta. Se o método não atingir essa expressão, poderá ser concluído de forma síncrona.
Para a maioria das APIs, prefira estes tipos de retorno:
- Use Task para operações assíncronas que não produzem um valor.
- Use Task<TResult> para operações assíncronas que produzem um valor.
- Use ValueTask ou ValueTask<TResult> somente quando as medidas mostrarem a pressão de alocação e quando os consumidores puderem lidar com as restrições de uso extra.
Mantenha a nomenclatura TAP previsível:
- Use o
Asyncsufixo para métodos que retornam tipos aguardados. - Não acrescente
Asynca métodos síncronos. - Adicione a nova sobrecarga
MethodNameAsyncjunto com o "MethodName" existente. Não remova ou renomeie a API síncrona. Manter os dois permite que os chamadores migrem em seu próprio ritmo sem uma mudança disruptiva.
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 no mínimo para que o método assíncrono possa retornar rapidamente. Os motivos para um retorno rápido incluem:
- É possível invocar os métodos assíncronos de segmentos de interface do usuário (UI), e qualquer trabalho síncrono longo pode prejudicar a resposta do aplicativo.
- Você pode iniciar vários métodos assíncronos simultaneamente. Portanto, qualquer trabalho de execução longa 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. A leitura de um fluxo em que a operação de leitura pode ser atendida por dados que já estão armazenados em buffer na memória é um exemplo desse cenário. Nesses casos, a operação pode ser concluída de forma síncrona e pode retornar uma tarefa que já está concluída.
Exceções
Um método assíncrono deve gerar uma exceção diretamente da chamada de método assíncrono apenas em resposta a um erro de uso. 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 causar um estado de erro (geralmente representado por uma ArgumentNullException exceção), você poderá 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á sendo executado para a tarefa retornada, mesmo que o método assíncrono seja concluído de forma síncrona antes que a tarefa seja retornada. Normalmente, uma tarefa contém no máximo uma exceção. No entanto, se a tarefa representar várias operações (por exemplo, WhenAll), várias exceções poderão ser associadas a uma única tarefa.
Ambiente de destino
Ao implementar um método TAP, você pode determinar onde ocorre a execução assíncrona. Você pode optar por executar a carga de trabalho no pool de threads, implementá-la usando E/S assíncrona (sem estar associada a um thread durante a maior parte da execução da operação), executá-la em um thread específico (como o thread da interface do usuário) ou usar qualquer número de contextos potenciais. Um método TAP pode até não ter nada para executar e pode apenas retornar um Task que representa a ocorrência de uma condição em outro lugar no sistema (por exemplo, uma tarefa que representa os dados que chegam a uma estrutura de dados na fila).
O chamador do método TAP pode bloquear a espera para o método TAP ser concluído com a espera síncrona na tarefa resultante ou pode executar código adicional (continuação) quando a operação assíncrona é concluída. O criador do código de continuação tem controle sobre onde esse código é executado. Você pode criar o código de continuação explicitamente, por meio de métodos na classe Task (por exemplo, ContinueWith) ou implicitamente, usando o suporte de linguagem baseado em continuações (por exemplo, await em C#, Await em Visual Basic, AwaitValue em F#).
Status da tarefa
A Task classe fornece um ciclo de vida para operações assíncronas e esse ciclo é representado pela TaskStatus enumeração. Para dar suporte a casos de canto de tipos derivados de Task e Task<TResult> e à separação da construção do agendamento, a classe Task expõe um método Start. As tarefas que são 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 é chamado nessas instâncias.
Todas as outras tarefas iniciam seu ciclo de vida em um estado ativo, o que significa que as operações assíncronas que representam já foram iniciadas e seu status de tarefa é um valor de enumeração diferente de TaskStatus.Created. Todas as tarefas que são retornadas dos métodos TAP devem ser ativadas. Se um método TAP usar internamente o construtor de uma tarefa para instanciar a tarefa a ser retornada, o método TAP precisa chamar Start no objeto Task antes de retorná-lo. Os consumidores de um método TAP podem presumir com segurança que a tarefa retornada está ativa e não devem tentar chamar Start em qualquer Task que é retornado de um método TAP. Chamar Start em uma tarefa ativa resulta em uma exceção InvalidOperationException.
Para obter diretrizes sobre preocupações relacionadas a duração e propriedade em operações de "fire-and-forget" após a ativação da tarefa, consulte Manter métodos assíncronos ativos.
Cancelamento (opcional)
No TAP, o cancelamento é opcional para implementadores de método assíncrono e consumidores de método assíncrono. Se uma operação permitir cancelamento, ela exporá uma sobrecarga do método assíncrono que aceita um token de cancelamento (CancellationToken instância). Por convenção, o parâmetro é nomeado 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 uma solicitação de cancelamento, ele poderá optar por atender a essa solicitação e cancelar a operação. Se a solicitação de cancelamento resultar no término prematuro do trabalho, o método TAP retornará uma tarefa que termina no Canceled estado; não há nenhum resultado disponível e nenhuma exceção será gerada. O estado Canceled é considerado um estado final (concluído) para uma tarefa, juntamente com os estados Faulted e RanToCompletion. Portanto, se uma tarefa estiver no Canceled estado, sua IsCompleted propriedade retornará true. Quando uma tarefa é concluída no estado Canceled, quaisquer continuações registradas com a tarefa são programadas ou executadas, a menos que uma opção de continuação, como NotOnCanceled, tenha sido especificada para recusar a continuação. Qualquer código que esteja aguardando de forma assíncrona uma tarefa cancelada por meio do uso de recursos de linguagem continua a ser executado, mas recebe uma OperationCanceledException ou uma exceção derivada dela. Código que está bloqueado de forma síncrona aguardando a tarefa por meio de métodos como Wait e WaitAll também continua a ser executado com uma exceção.
Se um token de cancelamento solicitar cancelamento antes do método TAP que aceita esse token ser chamado, o método TAP deverá retornar uma Canceled tarefa. 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 um resultado ou uma exceção ainda for produzido, a tarefa deverá terminar no estado RanToCompletion ou Faulted.
Para métodos assíncronos que expõem a capacidade de cancelamento em primeiro lugar, não é necessário fornecer 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. O código do consumidor que não deseja cancelamento pode chamar um método que aceita um CancellationToken e fornece None como o valor do argumento. None é funcionalmente equivalente ao padrão CancellationToken.
Relatórios de progresso (opcional)
Algumas operações assíncronas se beneficiam do fornecimento de notificações de progresso. Normalmente, use essas notificações para atualizar uma interface do usuário com informações sobre o progresso da operação assíncrona.
No TAP, manipule o progresso por meio de uma IProgress<T> interface. Passe essa interface para o método assíncrono como um parâmetro, geralmente chamado progress. Ao fornecer a interface de progresso no momento da chamada do método assíncrono, você ajuda a eliminar as condições de corrida resultantes do uso incorreto. Essas condições de corrida ocorrem quando os manipuladores de eventos são registrados incorretamente após o início da operação e perdem as atualizações. Mais importante, a interface de progresso suporta implementações de variação de progresso, conforme determinado pelo código consumidor. Por exemplo, o código consumidor só pode importar-se com a última atualização de progresso, pode desejar armazenar em buffer todas as atualizações, chamar uma ação para cada atualização ou controlar se a chamada é vinculada a um thread específico. Todas essas opções são alcançáveis usando diferentes implementações da interface, personalizadas de acordo com as necessidades do consumidor específico. Assim como ocorre com o cancelamento, as implementações de TAP devem fornecer um IProgress<T> parâmetro somente se a API der suporte a notificações de progresso.
Por exemplo, se o método ReadAsync discutido anteriormente nesse artigo for capaz de relatar o progresso intermediário na forma do número de bytes lidos até aqui, o retorno de chamada de progresso poderá ser uma interface IProgress<T>:
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 método FindFilesAsync retornar uma lista de todos os arquivos que atendem a um padrão de pesquisa específico, o retorno de chamada de progresso poderá fornecer uma estimativa da porcentagem de trabalho concluído e o conjunto atual de resultados parciais. Ele pode fornecer essas informações com uma tupla:
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 para a 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))
No último caso, o tipo de dados especial geralmente tem o sufixo ProgressInfo.
Se as implementações tap fornecerem sobrecargas que aceitam um progress parâmetro, elas deverão permitir que o argumento seja null. Se você passar null, nenhum progresso será relatado. 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 rapidamente o progresso. Ele 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 poderia optar por controlar retornos de chamada e gerar eventos em um contexto de sincronização capturado.
Implementações do IProgress<T>
O .NET fornece a Progress<T> classe, que implementa IProgress<T>. A Progress<T> classe é declarada da seguinte maneira:
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 ProgressChanged, o qual é gerado sempre que a operação assíncrona relata uma atualização de progresso. O evento ProgressChanged é gerado no objeto de SynchronizationContext que a instância de Progress<T> capta quando é criada. Se nenhum contexto de sincronização estiver disponível, um contexto padrão direcionado ao pool de threads será usado. Você pode registrar manipuladores com esse evento. Para conveniência, você também pode fornecer um único manipulador para o Progress<T> construtor. Esse manipulador se comporta como um manipulador 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ântica diferente.
Escolhendo as sobrecargas a serem fornecidas
Se uma implementação TAP utilizar tanto os parâmetros opcionais CancellationToken quanto 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 de TAP não fornecem recursos de cancelamento ou progresso, portanto, elas exigem um único método:
public Task MethodNameAsync(…);
Public MethodNameAsync(…) As Task
Se uma implementação de TAP oferecer suporte a cancelamento ou progresso, mas não a ambos, ela poderá 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 oferecer suporte a cancelamento e progresso, ela poderá expor todas as quatro sobrecargas. No entanto, ele pode fornecer apenas os dois seguintes:
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 intermediárias ausentes, os desenvolvedores podem passar None ou um padrão CancellationToken para o cancellationToken parâmetro e null para o progress parâmetro.
Para que todo uso do método TAP dê suporte a cancelamentos ou progressos, omita as sobrecargas que não aceitam o parâmetro relevante.
Se você decidir expor várias sobrecargas para tornar o cancelamento ou o progresso opcionais, as sobrecargas que não suportam o cancelamento ou o progresso devem se comportar como se tivessem passado None para cancelamento ou null para progresso para a sobrecarga que dá suporte a esses parâmetros.
Artigos relacionados
- Padrões de programação assíncronos – apresenta os três padrões para executar operações assíncronas: o TAP (Padrão Assíncrono baseado em tarefa), 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 maneiras: usando os compiladores C# e Visual Basic em Visual Studio, manualmente ou por meio de uma combinação do compilador e dos métodos manuais.
- Consumir o padrão assíncrono baseado em tarefa: descreve como usar tarefas e retornos de chamada para alcançar a espera sem bloquear.
- Interoperabilidade com outros padrões e tipos assíncronos – descreve como usar o TAP para implementar o Modelo de Programação Assíncrono (APM) e o Padrão Assíncrono baseado em evento (EAP).