Je suis en train de lire "Concurrence dans C # Cookbook" de Stephen Cleary, et j'ai remarqué la technique suivante:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask
est un appel à httpclient.GetStringAsync
et timeoutTask
exécute Task.Delay
.
Si le délai d’expiration n’est pas dépassé, alors downloadTask
est déjà terminé. Pourquoi faut-il faire une seconde attente au lieu de retourner downloadTask.Result
, étant donné que la tâche est déjà terminée?
Il y a déjà quelques bonnes réponses/commentaires ici, mais juste pour résumer ...
Il y a deux raisons pour lesquelles je préfère await
à Result
(ou Wait
). La première est que le traitement des erreurs est différent. await
n'emballe pas l'exception dans un AggregateException
. Idéalement, le code asynchrone ne devrait jamais avoir à traiter avec AggregateException
du tout, à moins qu'il ne veuille spécifiquement .
La deuxième raison est un peu plus subtile. Comme je le décris sur mon blog (et dans le livre), Result
/Wait
peut provoquer des blocages , et peut provoquer des blocages encore plus subtils lorsqu'il est utilisé dans une méthode async
. Ainsi, lorsque je lis le code et que je vois un Result
ou Wait
, c'est un indicateur d'avertissement immédiat. Le Result
/Wait
n'est correct que si vous êtes absolument certain que la tâche est déjà terminée. Non seulement cela est difficile à voir en un coup d'œil (dans le code du monde réel), mais c'est aussi plus fragile aux changements de code.
Cela ne veut pas dire que Result
/Wait
ne devrait jamais être utilisé. Je suis ces directives dans mon propre code:
await
.Result
/Wait
si le code l’appelle vraiment. Un tel usage devrait probablement comporter des commentaires.Result
et Wait
.Notez que (1) est de loin le cas courant, d’où ma tendance à utiliser await
partout et à traiter les autres cas comme des exceptions à la règle générale.
Cela a du sens si timeoutTask
est un produit de Task.Delay
, que je crois ce que c'est dans le livre.
Task.WhenAny
résultats Task<Task>
, où la tâche interne est l’une de celles que vous avez transmises en tant qu’arguments. Cela pourrait être réécrit comme ceci:
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
Dans les deux cas, étant donné que downloadTask
est déjà terminé, il existe une différence mineure entre return await downloadTask
et return downloadTask.Result
. C'est dans ce dernier que jettera AggregateException
qui enveloppe toute exception originale, comme l'a souligné @KirillShlenskiy dans les commentaires. Le premier voudrait juste renvoyer l'exception originale.
Dans les deux cas, où que vous gériez des exceptions, vous devriez quand même vérifier AggregateException
et ses exceptions internes pour obtenir la cause de l'erreur.