Task.Result é o mesmo que .GetAwaiter.GetResult ()?

328

Recentemente, eu estava lendo algum código que usa muitos métodos assíncronos, mas às vezes precisa executá-los de forma síncrona. O código faz:

Foo foo = GetFooAsync(...).GetAwaiter().GetResult();

É o mesmo que

Foo foo = GetFooAsync(...).Result;
Jay Bazuzi
fonte
8
Dos documentos de GetResult: "Este tipo e seus membros devem ser usados ​​pelo compilador." Outra pessoa não deveria estar usando.
gastador
32
Isso é chamado de "sincronização mais assíncrono", e se você não sabe como a tarefa é implementado pode ser uma realmente má idéia. Ele pode instantaneamente impasse em muitos casos (um async/ awaitmétodo em MVC, por exemplo)
Marc Gravell
10
Não
bloqueie
14
No mundo real, temos construtores, não temos interfaces de "aguardar" que precisamos implementar e recebemos métodos assíncronos em todos os lugares. Eu ficaria satisfeito em usar algo que simplesmente funciona sem que eu tenha que me perguntar por que é "perigoso", "não deve ser usado" ou "evitar a todo custo". Toda vez que eu tenho que mexer com async, acabo com uma dor de cabeça.
Larry

Respostas:

173

Bastante. Porém, uma pequena diferença: se Taskfalhar, GetResult()lançará apenas a exceção causada diretamente, enquanto Task.Resultlançará um AggregateException. No entanto, qual é o sentido de usar um desses quando é async? A opção 100x melhor é usar await.

Além disso, você não deve usar GetResult(). É para ser apenas para uso do compilador, não para você. Mas se você não quer o irritante AggregateException, use-o.

It'sNotALie.
fonte
27
@ JayBazuzi Não se sua estrutura de teste de unidade suportar testes de unidade assíncronos, o que eu acho que as versões mais recentes da maioria das estruturas suportam.
svick
15
@ JayBazuzi: MSTest, xUnit e NUnit todos suportam async Tasktestes de unidade, e já há algum tempo.
Stephen Cleary
18
pressionando 100x - é 1000x pior usar o wait, se você estiver adaptando o código antigo e o uso do wait precisar de uma reescrita.
preso
13
@AlexZhukovskiy: Eu discordo .
9788 Stephen Hawthys
15
The 100x better option is to use await.Eu odeio declarações como esta, se eu pudesse dar um tapa awaitna frente dela, eu faria. Mas, quando eu estou tentando obter o código assíncrono para trabalhar contra código não-async como o que muitas vezes acontece-me muito em Xamarin, eu acabar tendo que usar coisas como ContinueWithum monte, a fim de torná-lo não impasse a interface do usuário. Edit: Eu sei que isso é antigo, mas isso não alivia minha frustração ao encontrar respostas que afirmam isso sem alternativas para situações em que você não pode simplesmente usar await.
Thomas F.
147

Task.GetAwaiter().GetResult()é preferido Task.Waite Task.Resultporque propaga exceções em vez de agrupá-las em um arquivo AggregateException. No entanto, todos os três métodos causam o potencial de problemas de falta de deadlock e pool de encadeamentos. Todos eles devem ser evitados em favor de async/await.

A citação abaixo explica por que Task.Waite Task.Resultnão contém simplesmente o comportamento de propagação de exceção de Task.GetAwaiter().GetResult()(devido a uma "barra de compatibilidade muito alta").

Como mencionei anteriormente, temos uma barra de compatibilidade muito alta e, portanto, evitamos interromper as alterações. Como tal, Task.Waitmantém seu comportamento original de sempre quebrar. No entanto, você pode se encontrar em algumas situações avançadas em que deseja um comportamento semelhante ao bloqueio síncrono empregado por Task.Wait, mas em que deseja que a exceção original seja propagada sem quebra, em vez de envolvida em um AggregateException. Para conseguir isso, você pode direcionar o garçom da tarefa diretamente. Quando você escreve “ await task;”, o compilador converte isso no uso do Task.GetAwaiter()método, que retorna uma instância que possui um GetResult()método. Quando usado em uma tarefa com falha, GetResult()propagará a exceção original (é assim que " await task;" obtém seu comportamento). Você pode usar "task.GetAwaiter().GetResult()”Se você desejar chamar diretamente essa lógica de propagação.

https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/

" GetResult" Significa realmente "verifique a tarefa quanto a erros"

Em geral, tento o meu melhor para evitar o bloqueio síncrono de uma tarefa assíncrona. No entanto, existem algumas situações em que eu viole essa diretriz. Nessas condições raras, meu método preferido é GetAwaiter().GetResult()porque preserva as exceções da tarefa em vez de agrupá-las em um AggregateException.

http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html

Nitin Agarwal
fonte
3
Então, basicamente, Task.GetAwaiter().GetResult()é equivalente a await task. Presumo que a primeira opção seja usada quando o método não puder ser marcado com async(construtor por exemplo). Isso está correto? Se sim, então ele colide com a resposta top @ It'sNotALie
OlegI
5
@OlegI: Task.GetAwaiter().GetResult()é equivalente a mais Task.Waite Task.Result(em que todos os três vai bloquear de forma síncrona e têm o potencial para impasses), mas Task.GetAwaiter().GetResult()tem o comportamento de propagação excepção de tarefa esperam.
Nitin Agarwal
Você não pode evitar conflitos neste cenário com (Task) .ConfigureAwait (false) .GetAwaiter (). GetResult (); ?
Daniel Lorenz
3
@DanielLorenz: Veja a seguinte citação: "Usar o ConfigureAwait (false) para evitar conflitos é uma prática perigosa. Você precisaria usar o ConfigureAwait (false) para cada espera no fechamento transitivo de todos os métodos chamados pelo código de bloqueio, incluindo todos os terceiros - e código de terceiros. Usar o ConfigureAwait (false) para evitar conflitos é, na melhor das hipóteses, apenas um hack) ... a melhor solução é "Não bloqueie no código assíncrono". " - blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Nitin Agarwal
4
Eu não entendo. Task.Wait e Task.Result são quebrados por design? Por que eles não são obsoletos?
Osexpert
69

https://github.com/aspnet/Security/issues/59

"Uma última observação: você deve evitar usar Task.Resulte Task.Wait, tanto quanto possível, como sempre encapsular a exceção interna em uma AggregateExceptione substituir a mensagem por um genérico (um ou mais erros), o que torna a depuração mais difícil Mesmo que a versão shouldn síncrona. não seja usado com tanta frequência, você deve considerar fortemente o uso Task.GetAwaiter().GetResult()".

scyuo
fonte
20
A fonte mencionada aqui é alguém citando outra pessoa, sem uma referência. Considere o contexto: posso ver muitas pessoas cegamente usando GetAwaiter (). GetResult () em qualquer lugar depois de ler isso.
Jack Ukleja
2
Então não devemos usá-lo?
Tofutim
11
Se duas tarefas terminarem com uma exceção, você perderá a segunda neste cenário Task.WhenAll(task1, task2).GetAwaiter().GetResult();.
Monsenhor
Aqui está outro exemplo: github.com/aspnet/AspNetCore/issues/13611
George Chakhidze 4/04/19
33

Outra diferença é quando a asyncfunção retorna apenas em Taskvez de Task<T>então você não pode usar

GetFooAsync(...).Result;

Enquanto que

GetFooAsync(...).GetAwaiter().GetResult();

ainda funciona.

Eu sei que o código de exemplo na pergunta é para o caso Task<T>, no entanto, a pergunta é feita geralmente.

Nuri Tasdemir
fonte
1
Isso não é verdade. Confira o meu violino que usa exatamente esta construção: dotnetfiddle.net/B4ewH8
wojciech_rak
3
@wojciech_rak No seu código, você está usando Resultcom GetIntAsync()que retornos Task<int>não apenas Task. Eu sugiro que você leia minha resposta novamente.
Nuri Tasdemir
1
Você está certo, no começo eu entendi que você responde que não pode GetFooAsync(...).Result dentro de uma função que retorna Task. Isso agora faz sentido, já que não há Propriedades nulas em C # ( Task.Resulté uma propriedade), mas é claro que você pode chamar um método nulo.
Wojciech_rak
22

Como já mencionado, se você pode usar await. Se você precisar executar o código de forma síncrona como você mencionou .GetAwaiter().GetResult(), .Resultou.Wait() houver um risco de conflito, como muitos disseram em comentários / respostas. Como a maioria de nós gosta de oneliners, você pode usá-los para.Net 4.5<

Adquirindo um valor por meio de um método assíncrono:

var result = Task.Run(() => asyncGetValue()).Result;

Chamando de forma síncrona um método assíncrono

Task.Run(() => asyncMethod()).Wait();

Nenhum problema de conflito ocorrerá devido ao uso de Task.Run.

Fonte:

https://stackoverflow.com/a/32429753/3850405

Ogglas
fonte
1

Se uma tarefa falhar, a exceção será lançada novamente quando o código de continuação chamar waititer.GetResult (). Em vez de chamar GetResult, poderíamos simplesmente acessar a propriedade Result da tarefa. O benefício de chamar GetResult é que, se a tarefa falhar, a exceção será lançada diretamente sem ser agrupada em AggregateException, permitindo blocos de captura mais simples e limpos.

Para tarefas não genéricas, GetResult () possui um valor de retorno nulo. Sua função útil é, então, apenas relançar exceções.

fonte: c # 7.0 em poucas palavras

Ali Abdollahi
fonte