Quelles sont les différences entre l'utilisation de Parallel.ForEach ou de Task.Run () pour démarrer un ensemble de tâches de manière asynchrone?
Version 1:
List<string> strings = new List<string> { "s1", "s2", "s3" };
Parallel.ForEach(strings, s =>
{
DoSomething(s);
});
Version 2:
List<string> strings = new List<string> { "s1", "s2", "s3" };
List<Task> Tasks = new List<Task>();
foreach (var s in strings)
{
Tasks.Add(Task.Run(() => DoSomething(s)));
}
await Task.WhenAll(Tasks);
c#
async-await
parallel.foreach
Petter T
la source
la source
Task.WaitAll
place deTask.WhenAll
.Réponses:
Dans ce cas, la deuxième méthode attendra de manière asynchrone que les tâches se terminent au lieu de se bloquer.
Cependant, il y a un inconvénient à utiliser
Task.Run
dans une boucle - AvecParallel.ForEach
, il y a unPartitioner
qui est créé pour éviter de faire plus de tâches que nécessaire.Task.Run
fera toujours une seule tâche par élément (puisque vous faites cela), mais lesParallel
lots de classe fonctionnent donc vous créez moins de tâches que le nombre total d'éléments de travail. Cela peut fournir des performances globales nettement meilleures, en particulier si le corps de la boucle a une petite quantité de travail par élément.Si tel est le cas, vous pouvez combiner les deux options en écrivant:
Notez que cela peut également être écrit sous cette forme plus courte:
la source
DoSomething
c'estasync void DoSomething
?async Task DoSomething
?La première version bloquera de manière synchrone le thread appelant (et exécutera certaines des tâches dessus).
S'il s'agit d'un thread d'interface utilisateur, cela gèlera l'interface utilisateur.
La deuxième version exécutera les tâches de manière asynchrone dans le pool de threads et libérera le thread appelant jusqu'à ce qu'elles soient terminées.
Il existe également des différences dans les algorithmes de planification utilisés.
Notez que votre deuxième exemple peut être raccourci en
la source
await Task.WhenAll(strings.Select(async s => await Task.Run(() => DoSomething(s)));
? J'ai eu des problèmes lors du retour de tâches (au lieu d'attendre), en particulier lorsque des instructions commeusing
étaient impliquées pour éliminer des objets.J'ai fini par faire ça, car c'était plus facile à lire:
la source
J'ai vu Parallel.ForEach utilisé de manière inappropriée, et j'ai pensé qu'un exemple dans cette question serait utile.
Lorsque vous exécutez le code ci-dessous dans une application console, vous verrez comment les tâches exécutées dans Parallel.ForEach ne bloquent pas le thread appelant. Cela peut convenir si vous ne vous souciez pas du résultat (positif ou négatif), mais si vous avez besoin du résultat, vous devez vous assurer d'utiliser Task.WhenAll.
Voici le résultat:
Conclusion:
L'utilisation de Parallel.ForEach avec une tâche ne bloquera pas le thread appelant. Si vous vous souciez du résultat, assurez-vous d'attendre les tâches.
~ Acclamations
la source