Je lisais récemment un code qui utilise beaucoup de méthodes asynchrones, mais qui doit parfois les exécuter de manière synchrone. Le code fait:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
Est-ce la même chose que
Foo foo = GetFooAsync(...).Result;
?
Plutôt. Une petite différence cependant: si la Task
échoue, GetResult()
lève simplement l'exception directement, alors que Task.Result
lève une AggregateException
. Cependant, à quoi sert-il d'utiliser l'un ou l'autre lorsque c'est async
? La meilleure option est d'utiliser await
.
De plus, vous n'êtes pas censé utiliser GetResult()
. Il est conçu pour être utilisé uniquement par le compilateur, pas pour vous. Mais si vous ne voulez pas la AggregateException
ennuyeuse, utilisez-la.
Task.GetAwaiter().GetResult()
est préférable à Task.Wait
et Task.Result
car il propage des exceptions plutôt que de les encapsuler dans une AggregateException
. Cependant, les trois méthodes peuvent entraîner des problèmes d'interblocage et doivent être évitées en faveur de async/await
.
La citation ci-dessous explique pourquoi Task.Wait
et Task.Result
ne contiennent pas simplement le comportement de propagation d'exception de Task.GetAwaiter().GetResult()
(en raison d'une "barre de compatibilité très élevée").
Comme je l’ai mentionné précédemment, nous avons une barre de compatibilité très élevée, ce qui évite d’interrompre les modifications. En tant que tel,
Task.Wait
conserve son comportement d'origine d'emballage toujours. Cependant, vous pouvez vous trouver dans certaines situations avancées dans lesquelles vous souhaitez un comportement similaire au blocage synchrone utilisé parTask.Wait
, mais dans lequel vous souhaitez que l'exception d'origine soit propagée non encapsulée plutôt que d'être encapsulée dans uneAggregateException
. Pour ce faire, vous pouvez cibler directement le serveur en attente de la tâche. Lorsque vous écrivez «await task;
», le compilateur traduit cela en utilisation de la méthodeTask.GetAwaiter()
, qui renvoie une instance contenant une méthodeGetResult()
. Lorsqu'il est utilisé sur une tâche défaillante,GetResult()
propage l'exception d'origine (c'est ainsi que “await task;
” obtient son comportement). Vous pouvez donc utiliser «task.GetAwaiter().GetResult()
» si vous souhaitez appeler directement cette logique de propagation.
https://blogs.msdn.Microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/
"
GetResult
" signifie en fait "vérifier la tâche pour les erreurs"En général, je fais de mon mieux pour éviter le blocage synchrone sur une tâche asynchrone. Cependant, il y a une poignée de situations dans lesquelles je viole cette directive. Dans ces conditions rares, ma méthode préférée est
GetAwaiter().GetResult()
car elle préserve les exceptions de tâches au lieu de les encapsuler dans uneAggregateException
.
http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html
https://github.com/aspnet/Security/issues/59
"Une dernière remarque: évitez autant que possible d'utiliser
Task.Result
etTask.Wait
comme Elles encapsulent toujours l'exception interne dans unAggregateException
et remplacent le message par un message générique (une ou plusieurs erreurs se sont produites). , ce qui rend le débogage plus difficile. Même si la version synchrone ne doit pas être utilisée aussi souvent, vous devriez vivement envisager d’utiliser plutôtTask.GetAwaiter().GetResult()
. "
Une autre différence réside dans le fait que la fonction async
renvoie uniquement Task
au lieu de Task<T>
, vous ne pouvez pas utiliser
GetFooAsync(...).Result;
Tandis que
GetFooAsync(...).GetAwaiter().GetResult();
fonctionne encore.
Je sais que l'exemple de code dans la question concerne le cas Task<T>
, mais la question est posée de manière générale.
Si une tâche échoue, l'exception est à nouveau levée lorsque le code de continuation appelle waiter.GetResult (). Plutôt que d'appeler GetResult, nous pourrions simplement accéder à la propriété Result de la tâche. L'avantage d'appeler GetResult est que si la tâche échoue, l'exception est levée directement sans être encapsulée dans AggregateException, ce qui permet des blocs d'interception plus simples et plus nets.
Pour les tâches non génériques, GetResult () a une valeur de retour vide. Sa fonction utile est alors uniquement de renvoyer des exceptions.
source: c # 7.0 en quelques mots
Comme déjà mentionné si vous pouvez utiliser await
. Si vous devez exécuter le code de manière synchrone, comme vous mentionnez .GetAwaiter().GetResult()
, .Result
ou .Wait()
constitue un risque de blocage, comme beaucoup l'ont indiqué dans des commentaires/réponses. Comme la plupart d’entre nous aiment les oneliners, vous pouvez les utiliser pour .Net 4.5<
Acquérir une valeur via une méthode asynchrone:
var result = Task.Run(() => asyncGetValue()).Result;
Appeler de manière synchronisée une méthode asynchrone
Task.Run(() => asyncMethod()).Wait();
Aucun problème d'interblocage ne se produira en raison de l'utilisation de Task.Run
.
La source: