web-dev-qa-db-fra.com

Attendez que la tâche terminée soit identique à la tâche.Résultat?

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?

102
julio.g

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:

  1. Le code asynchrone dans une application ne peut utiliser que await.
  2. Le code d’utilité asynchrone (dans une bibliothèque) peut parfois utiliser Result/Wait si le code l’appelle vraiment. Un tel usage devrait probablement comporter des commentaires.
  3. Le code de tâche parallèle peut utiliser 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.

142
Stephen Cleary

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.

11
noseratio