Pourquoi devrais-je préfère unique 'attendent Tâche.WhenAll " sur plusieurs attend?
Dans le cas où je ne se soucient pas de l'ordre de l'achèvement de la tâche et juste besoin de toutes pour terminer, dois-je toujours utiliser await Task.WhenAll
au lieu de plusieurs await
? e.g, 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();
}
}
}
- Que faire si vous ne savez pas vraiment comment beaucoup de tâches que vous devez faire en parallèle? Que faire si vous avez 1000 de tâches à exécuter? Le premier va pas être très lisible
await t1; await t2; ....; await tn
=> le second est toujours le meilleur choix dans les deux cas - Votre commentaire a du sens. J'essayais juste clarifier quelque chose pour moi-même, liée à une autre question, j'ai récemment answered. Dans ce cas, il y avait 3 tâches.
Vous devez vous connecter pour publier un commentaire.
Oui, l'utilisation
WhenAll
parce qu'elle se propage à toutes les erreurs à la fois. Avec les multiples vous attend, vous perdez des erreurs si l'une des premières attend lancers.Une autre différence importante est que
WhenAll
va attendre que toutes les tâches à effectuer , même en présence de défaillances (défectueuses ou de l'annulation de tâches). En attendant manuellement dans la séquence serait la cause inattendue de la simultanéité parce que la partie de votre programme qui veut attendre va continuer tôt.Je pense que ça rend la lecture du code plus facile parce que la sémantique que vous voulez sont directement documentée dans le code.
await
son résultat.Ma compréhension est que la principale raison de préférer
Task.WhenAll
à plusieursawait
s est la performance /la tâche "barattage": leDoWork1
méthode fait quelque chose comme ceci:En revanche,
DoWork2
fait ceci:Si c'est une grosse affaire pour votre cas particulier est, bien sûr, "en fonction du contexte" (pardonnez le calembour).
Une méthode asynchrone est mis en œuvre comme une machine d'état. Il est possible d'écrire des méthodes pour qu'elles ne sont pas compilées dans des machines d'états, c'est souvent désigné comme un fast-track méthode asynchrone. Ceux-ci peuvent être mis en œuvre comme suit:
Lors de l'utilisation de
Task.WhenAll
il est possible de maintenir ce fast-track code tout en veillant à ce que l'appelant est en mesure d'attendre pour toutes les tâches à accomplir, par exemple:Autres réponses à cette question d'offrir des raisons techniques
await Task.WhenAll(t1, t2, t3);
est préféré. Cette réponse aura pour but de le regarder d'un côté plus doux (dont @usr fait allusion), tandis que toujours à venir à la même conclusion.await Task.WhenAll(t1, t2, t3);
est une approche plus fonctionnelle, comme il le déclare l'intention et de l'est atomique.Avec
await t1; await t2; await t3;
, il n'y a rien qui empêche un coéquipier (ou peut-être même votre soi futur!) à partir de l'ajout de code entre l'individuawait
consolidés. Bien sûr, vous avez compressé à une ligne essentiellement à accomplir, mais qui ne résout pas le problème. En outre, il est généralement une mauvaise forme au sein d'une équipe d'inclure plusieurs instructions sur une même ligne de code, comme il peut rendre le fichier source est plus difficile pour les yeux de l'homme à numériser.Tout simplement,
await Task.WhenAll(t1, t2, t3);
est plus facile à gérer, qu'il communique votre intention plus claire et moins vulnérable aux particuliers bugs qui peuvent sortir de mises à jour du code, ou même juste fusionne mal tourné.(Avertissement: Cette réponse est tiré ou inspiré de Ian Griffiths TPL Asynchrone en cours sur Pluralsight)
Une autre raison de préférer WhenAll est la gestion des exceptions.
Supposons que vous ayez un bloc try-catch sur votre DoWork méthodes, et supposons qu'ils appelaient différents DoTask méthodes:
Dans ce cas, si tous les 3 tâches de lancer des exceptions, seul le premier sera pris. Un peu plus tard exception sera perdu. I. e. si t2 et t3, déclenche une exception que t2 sera à maîtriser; etc. Ensuite les tâches exceptions de passer inaperçues.
Où, comme dans le WhenAll - si l'un ou l'ensemble des tâches de la faute, de la tâche obtenue contiendra toutes les exceptions. Le mot clé await toujours re-jette la première exception. Alors que les autres exceptions sont effectivement encore inaperçu. Une façon de surmonter ce problème est d'ajouter un vide continuation, une fois la tâche WhenAll et de mettre de l'attendre là. De cette façon, si la tâche échoue, le résultat propriété lancera le plein d'Agrégation Exception: