Acabei de adquirir o VS2012 e tentar entender async
.
Digamos que eu tenho um método que busca algum valor de uma fonte de bloqueio. Não quero que o chamador do método seja bloqueado. Eu poderia escrever o método para receber um retorno de chamada que é chamado quando o valor chega, mas como estou usando o C # 5, decido tornar o método assíncrono para que os chamadores não precisem lidar com retornos de chamada:
// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
Aqui está um método de exemplo que o chama. Se PromptForStringAsync
não fosse assíncrono, esse método exigiria o aninhamento de um retorno de chamada em um retorno de chamada. Com o async, escrevo meu método dessa maneira muito natural:
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}
Por enquanto, tudo bem. O problema é quando eu chamo GetNameAsync:
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
O ponto principal GetNameAsync
é que é assíncrono. Não quero que ele bloqueie, porque quero voltar para o MainWorkOfApplicationIDontWantBlocked o mais rápido possível e permitir que GetNameAsync faça suas coisas em segundo plano. No entanto, chamar dessa maneira me dá um aviso do compilador na GetNameAsync
linha:
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
Estou perfeitamente ciente de que "a execução do método atual continua antes que a chamada seja concluída". Esse é o objetivo do código assíncrono, certo?
Prefiro que meu código seja compilado sem avisos, mas não há nada para "corrigir" aqui, porque o código está fazendo exatamente o que pretendo fazer. Posso me livrar do aviso armazenando o valor de retorno de GetNameAsync
:
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
Mas agora eu tenho código supérfluo. O Visual Studio parece entender que fui forçado a escrever esse código desnecessário, porque suprime o aviso "valor nunca usado" normal.
Também posso me livrar do aviso envolvendo GetNameAsync em um método que não é assíncrono:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
Mas esse é um código ainda mais supérfluo. Portanto, tenho que escrever um código que não preciso ou tolero um aviso desnecessário.
Existe algo sobre meu uso de assíncrono errado aqui?
fonte
PromptForStringAsync
você trabalha mais do que precisa; basta retornar o resultado deTask.Factory.StartNew
. Já é uma tarefa cujo valor é a sequência inserida no console. Não há necessidade de esperar o retorno do resultado; isso não agrega nenhum novo valor.GetNameAsync
fornecer o nome completo que foi fornecida pelo usuário (ou sejaTask<Name>
, ao invés de apenas retornar umTask
?DoStuff
Poderia, então, armazenar essa tarefa, e querawait
que após o outro método, ou mesmo passar a tarefa para a outra método para que ele pudesseawait
ouWait
em algum lugar dentro dele de implementação.async
palavra - chaveRespostas:
Se você realmente não precisa do resultado, pode simplesmente mudar a
GetNameAsync
assinatura para retornarvoid
:Considere a resposta a uma pergunta relacionada: Qual é a diferença entre retornar nulo e retornar uma tarefa?
Atualizar
Se você precisar do resultado, poderá alterar
GetNameAsync
para retornar, digamosTask<string>
:E use-o da seguinte maneira:
fonte
GetNameAsync
não retornam nenhum valor (exceto o resultado em si, é claro).void
, ele não tem como saber quando está pronto. Foi isso que eu quis dizer quando disse "o resultado" no meu comentário anterior.async void
método, exceto para manipuladores de eventos.async void
qualquer exceção que você não captura travará o processo, mas no .net 4.5 ele continuará em execução.Estou muito atrasado para esta discussão, mas também há a opção de usar a
#pragma
diretiva pré-processador. Eu tenho um código assíncrono aqui e ali que, explicitamente, não quero aguardar em algumas condições, e não gosto de avisos e variáveis não utilizadas, assim como o resto de vocês:O
"4014"
vem desta página MSDN: Compiler Aviso (nível 1) CS4014 .Consulte também o aviso / resposta de @ ryan-horath aqui https://stackoverflow.com/a/12145047/928483 .
Atualização para C # 7.0
O C # 7.0 adiciona um novo recurso, descarte variáveis: Descartes - Guia de C # , que também pode ajudar nesse sentido.
fonte
var
, basta escrever_ = SomeMethodAsync();
Não gosto particularmente das soluções que atribuem a tarefa a uma variável não utilizada ou alteram a assinatura do método para retornar nulo. O primeiro cria código supérfluo e não intuitivo, enquanto o último pode não ser possível se você estiver implementando uma interface ou tiver outro uso da função em que deseja usar a tarefa retornada.
Minha solução é criar um método de extensão da tarefa, chamado DoNotAwait () que não faz nada. Isso não apenas suprimirá todos os avisos, ReSharper ou outros, mas também tornará o código mais compreensível e indicará aos futuros mantenedores do seu código que você realmente pretendia que a chamada não fosse aguardada.
Método de extensão:
Uso:
Editado para adicionar: isso é semelhante à solução de Jonathan Allen, em que o método de extensão iniciaria a tarefa se ainda não tiver sido iniciado, mas eu prefiro ter funções de finalidade única para que a intenção do chamador seja completamente clara.
fonte
async void
É RUIM!O que eu sugiro é que você execute explicitamente o
Task
método anônimo ...por exemplo
Ou, se você quiser bloquear, aguarde o método anônimo
No entanto, se o seu
GetNameAsync
método precisar interagir com a interface do usuário ou com qualquer coisa vinculada à interface do usuário (WINRT / MVVM, estou olhando para você), ficará um pouco mais engraçado =)Você precisará passar a referência ao expedidor da interface do usuário desta maneira ...
E então, no seu método assíncrono, você precisará interagir com a interface do usuário ou com os elementos vinculados à interface do usuário, como o expedidor ...
fonte
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
Isso também cria um novo encadeamento, enquanto um novo encadeamento não será necessariamente criado apenas com async / waitit.Isto é o que estou fazendo atualmente:
Onde
RunConcurrently
é definido como ...https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs
https://www.nuget.org/packages/Tortuga.Anchor/
fonte
public static void Forget(this Task task) { }
async Task
. Algumas tarefas precisam ser iniciadas manualmente.De acordo com o artigo da Microsoft sobre este aviso, você pode resolvê-lo simplesmente atribuindo a tarefa retornada a uma variável. Abaixo está uma tradução do código fornecido no exemplo da Microsoft:
Observe que isso resultará na mensagem "A variável local nunca é usada" no ReSharper.
fonte
Task
As funções de retorno devem serawait
-ed, a menos que você tenha uma boa razão para não fazê- lo. Não há nenhuma razão aqui para descartar a tarefa seria melhor do que a resposta já aceita do uso de umasync void
método.async void
apresenta problemas sérios no tratamento de erros e resulta em código não testável (consulte meu artigo do MSDN ). Seria muito melhor usar a variável - se você tiver certeza absoluta de que deseja que as exceções sejam engolidas silenciosamente. O mais provável é que o op queira iniciar dois seTask
depois executar umawait Task.WhenAll
.async void DoNotWait(Task t) { await t; }
método auxiliar simples pode ser usado para evitar as desvantagens dosasync void
métodos que você descreve. (E eu não acho queTask.WhenAll
é o que o OP quer, mas poderia muito bem ser.)Aqui, uma solução simples.
Saudações
fonte
É o seu exemplo simplificado que causa o código supérfluo. Normalmente, você deseja usar os dados que foram buscados a partir da fonte de bloqueio em algum momento do programa, para que você deseje o resultado de volta para que seja possível acessar os dados.
Se você realmente tem algo que acontece totalmente isolado do resto do programa, a assíncrona não seria a abordagem correta. Basta iniciar um novo thread para essa tarefa.
fonte
async
foi projetado para limpar ( por exemplo )MethodWithCallback((result1) => { Use(result1); MethodWithCallback((result2) => { Use(result1,result2); })
Mesmo neste exemplo trivial, é muito chato analisar. Com assíncrona, código equivalente é gerado para mim quando eu escrevoresult1 = await AsyncMethod(); Use(result1); result2 = await AsyncMethod(); Use(result1,result2);
O que é muito mais fácil de ler (ainda que nem são muito legível esmagado juntos em este comentário!)Use
.Deseja realmente ignorar o resultado? como na inclusão de ignorar exceções inesperadas?
Caso contrário, você pode querer dar uma olhada nesta pergunta: Abordar o fogo e esquecer ,
fonte
Se você não deseja alterar a assinatura do método para retornar
void
(como retornarvoid
sempre deve ser um nulo ), você pode usar o recurso de descarte do C # 7.0+ como este, que é um pouco melhor do que atribuir a uma variável (e deve remover a maioria dos outros avisos das ferramentas de validação de fonte):fonte