Au cas où je ne me soucierais pas de l'ordre d'achèvement des tâches et que j'aurais juste besoin de toutes les terminer, dois-je toujours utiliser await Task.WhenAll
au lieu de plusieurs await
? par exemple, est DoWork2
ci - dessous une méthode préférée pour DoWork1
(et pourquoi?):
using System;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static async Task<string> DoTaskAsync(string name, int timeout)
{
var start = DateTime.Now;
Console.WriteLine("Enter {0}, {1}", name, timeout);
await Task.Delay(timeout);
Console.WriteLine("Exit {0}, {1}", name, (DateTime.Now - start).TotalMilliseconds);
return name;
}
static async Task DoWork1()
{
var t1 = DoTaskAsync("t1.1", 3000);
var t2 = DoTaskAsync("t1.2", 2000);
var t3 = DoTaskAsync("t1.3", 1000);
await t1; await t2; await t3;
Console.WriteLine("DoWork1 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static async Task DoWork2()
{
var t1 = DoTaskAsync("t2.1", 3000);
var t2 = DoTaskAsync("t2.2", 2000);
var t3 = DoTaskAsync("t2.3", 1000);
await Task.WhenAll(t1, t2, t3);
Console.WriteLine("DoWork2 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static void Main(string[] args)
{
Task.WhenAll(DoWork1(), DoWork2()).Wait();
}
}
}
await t1; await t2; ....; await tn
=> le second est toujours le meilleur choix dans les deux casRéponses:
Oui, à utiliser
WhenAll
car il propage toutes les erreurs à la fois. Avec l'attente multiple, vous perdez des erreurs si l'une des attentes précédentes est lancée.Une autre différence importante est qu'il
WhenAll
attendra que toutes les tâches soient terminées, même en présence de pannes (tâches défectueuses ou annulées). Attendre manuellement dans l'ordre entraînerait une concurrence inattendue car la partie de votre programme qui veut attendre continuera en fait tôt.Je pense que cela facilite également la lecture du code car la sémantique souhaitée est directement documentée dans le code.
la source
await
son résultat.Je crois comprendre que la principale raison de préférer
Task.WhenAll
plusieursawait
s est le «barattage» des performances / tâches: laDoWork1
méthode fait quelque chose comme ceci:En revanche,
DoWork2
fait ceci:La question de savoir si cela est suffisamment important pour votre cas particulier dépend, bien sûr, du contexte (pardonnez le jeu de mots).
la source
Une méthode asynchrone est implémentée en tant que machine à états. Il est possible d'écrire des méthodes afin qu'elles ne soient pas compilées dans des machines à états, ce qui est souvent appelé une méthode asynchrone accélérée. Ceux-ci peuvent être implémentés comme ceci:
Lors de l'utilisation,
Task.WhenAll
il est possible de conserver ce code accéléré tout en garantissant que l'appelant est capable d'attendre que toutes les tâches soient terminées, par exemple:la source
(Avertissement: Cette réponse est tirée / inspirée du cours TPL Async d'Ian Griffiths sur Pluralsight )
Une autre raison de préférer WhenAll est la gestion des exceptions.
Supposons que vous ayez un bloc try-catch sur vos méthodes DoWork, et supposons qu'elles appelaient différentes méthodes DoTask:
Dans ce cas, si les 3 tâches lancent des exceptions, seule la première sera interceptée. Toute exception ultérieure sera perdue. Ie si t2 et t3 lèvent une exception, seul t2 sera intercepté; etc. Les exceptions de tâches suivantes ne seront pas observées.
Où comme dans WhenAll - si une ou toutes les tâches échouent, la tâche résultante contiendra toutes les exceptions. Le mot clé await renvoie toujours toujours la première exception. Donc, les autres exceptions sont encore effectivement inobservées. Une façon de surmonter ce problème consiste à ajouter une suite vide après la tâche WhenAll et à y placer l'attente. De cette façon, si la tâche échoue, la propriété de résultat lèvera l'exception d'agrégation complète:
la source
Les autres réponses à cette question offrent des raisons techniques pour lesquelles il
await Task.WhenAll(t1, t2, t3);
est préférable. Cette réponse visera à la regarder d'un côté plus doux (auquel @usr fait allusion) tout en arrivant à la même conclusion.await Task.WhenAll(t1, t2, t3);
est une approche plus fonctionnelle, car elle déclare l'intention et est atomique.Avec
await t1; await t2; await t3;
, rien n'empêche un coéquipier (ou peut-être même votre futur moi!) D'ajouter du code entre lesawait
déclarations individuelles . Bien sûr, vous l'avez compressé en une seule ligne pour accomplir cela, mais cela ne résout pas le problème. En outre, il est généralement mauvais dans une équipe d'inclure plusieurs instructions sur une ligne de code donnée, car cela peut rendre le fichier source plus difficile à scanner pour les yeux humains.En termes simples, il
await Task.WhenAll(t1, t2, t3);
est plus facile à maintenir, car il communique votre intention plus clairement et est moins vulnérable aux bogues particuliers qui peuvent provenir de mises à jour bien intentionnées du code, ou même simplement de fusions qui ont mal tourné.la source