As respostas de SLaks e Killercam são boas; Eu pensei em adicionar um pouco mais de contexto.
Sua primeira pergunta é essencialmente sobre quais métodos podem ser marcados async
.
Um método marcado como async
pode retornar void
, Task
ou Task<T>
. Quais são as diferenças entre eles?
Um Task<T>
método assíncrono de retorno pode ser aguardado e, quando a tarefa for concluída, ele oferecerá um T.
Um Task
método assíncrono de retorno pode ser aguardado e, quando a tarefa for concluída, a continuação da tarefa está agendada para execução.
Um void
método assíncrono retornado não pode ser aguardado; é um método "dispare e esqueça". Funciona de forma assíncrona e você não tem como saber quando isso é feito. Isso é mais do que um pouco estranho; como o SLaks diz, normalmente você faria isso apenas ao criar um manipulador de eventos assíncrono. O evento é disparado, o manipulador é executado; ninguém vai "aguardar" a tarefa retornada pelo manipulador de eventos, porque os manipuladores de eventos não retornam tarefas e, mesmo retornando, que código usaria a tarefa para alguma coisa? Geralmente, não é o código do usuário que transfere o controle para o manipulador em primeiro lugar.
Sua segunda pergunta, em um comentário, é essencialmente sobre o que pode ser await
editado:
Que tipos de métodos podem ser await
editados? Um método de retorno nulo pode ser await
editado?
Não, um método de retorno nulo não pode ser aguardado. O compilador se traduz await M()
em uma chamada para M().GetAwaiter()
, onde GetAwaiter
pode ser um método de instância ou um método de extensão. O valor esperado deve ser aquele pelo qual você pode obter um garçom; claramente, um método de retorno nulo não produz um valor a partir do qual você pode obter um garçom.
Task
métodos de retorno podem produzir valores esperáveis. Prevemos que terceiros desejarão criar suas próprias implementações de Task
objetos semelhantes que podem ser aguardados, e você poderá aguardá-los. No entanto, você não poderá declarar async
métodos que retornam nada void
, exceto , Task
ou Task<T>
.
(ATUALIZAÇÃO: Minha última frase pode ser falsificada por uma versão futura do C #; existe uma proposta para permitir tipos de retorno diferentes dos tipos de tarefas para métodos assíncronos.)
(ATUALIZAÇÃO: o recurso mencionado acima chegou ao C # 7.)
async void
Os métodos levantam sua exceção sobre oSynchronizationContext
que estava ativo no momento em que começaram a executar. Isso é semelhante ao comportamento dos manipuladores de eventos (síncronos). @DrewMarsh: aUnobservedTaskException
configuração e o tempo de execução se aplicam apenas aos métodos de tarefa assíncrona "disparar e esquecer" , não aosasync void
métodos.Caso o chamador deseje aguardar a tarefa ou adicionar uma continuação.
De fato, o único motivo para retornar
void
é se você não pode retornarTask
porque está escrevendo um manipulador de eventos.fonte
void
, você não poderá acessar a tarefa que ele gera. (Na verdade, eu não tenho certeza se ele mesmo gera umTask
em tudo)Os métodos retornando
Task
eTask<T>
são composíveis - o que significa que você pode utilizá-await
los dentro de umasync
método.async
Os métodos que retornamvoid
não são composíveis, mas têm duas outras propriedades importantes:O segundo ponto é importante quando você lida com um contexto que mantém uma contagem de operações assíncronas pendentes.
O contexto do ASP.NET é um desses contextos; se você usar
Task
métodos assíncronos sem aguardá-los de umvoid
método assíncrono , a solicitação do ASP.NET será concluída muito cedo.Outro contexto é o
AsyncContext
que escrevi para testes de unidade (disponível aqui ) - oAsyncContext.Run
método rastreia a contagem de operações pendentes e retorna quando é zero.fonte
Type
Task<T>
é o tipo de cavalo de trabalho da Task Parallel Library (TPL), representa o conceito de "algum trabalho / job que produzirá um resultado do tipoT
no futuro". O conceito de "trabalho que será concluído no futuro, mas não retornará resultado" é representado pelo tipo de tarefa não genérico.Precisamente como o resultado do tipo
T
será produzido e os detalhes da implementação de uma tarefa específica; o trabalho pode ser distribuído para outro processo na máquina local, para outro encadeamento etc. As tarefas TPL são tipicamente distribuídas para encadeamentos de trabalho de um conjunto de encadeamentos no processo atual, mas esse detalhe da implementação não é fundamental para oTask<T>
tipo; em vez disso, aTask<T>
pode representar qualquer operação de alta latência que produza aT
.Com base no seu comentário acima:
A
await
expressão significa "avaliar esta expressão para obter um objeto que representa um trabalho que no futuro produzirá um resultado. Registre o restante do método atual como retorno de chamada associado à continuação dessa tarefa. Depois que essa tarefa for produzida e a chamada de retorno estiver inscrito, retorne imediatamente o controle ao meu interlocutor ". Isso se opõe / contrasta com uma chamada de método regular, que significa "lembre-se do que você está fazendo, execute esse método até que esteja completamente concluído e, em seguida, continue de onde parou, agora sabendo o resultado do método".Edit: Eu deveria citar o artigo de Eric Lippert em outubro de 2011 MSDN Magazine, pois isso foi uma grande ajuda para mim para entender essas coisas em primeiro lugar.
Para mais informações e páginas em branco, consulte aqui .
Espero que este seja de alguma ajuda.
fonte