Was sind die Unterschiede zwischen der Verwendung von Parallel.ForEach oder Task.Run (), um eine Reihe von Aufgaben asynchron zu starten?
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.
quelle
quelle
Task.WaitAll
stattdessen verwenden würdenTask.WhenAll
.Antworten:
In diesem Fall wartet die zweite Methode asynchron auf den Abschluss der Aufgaben, anstatt sie zu blockieren.
Jedoch gibt es einen Nachteil verwenden
Task.Run
in einer schlaufen mitParallel.ForEach
, gibt es eine ,Partitioner
die zu vermeiden , erstellt wird , mehr Aufgaben als nötig zu machen.Task.Run
Es wird immer eine einzelne Aufgabe pro Element erstellt (da Sie dies tun), aber dieParallel
Klassenstapel funktionieren, sodass Sie weniger Aufgaben als die gesamten Arbeitselemente erstellen. Dies kann zu einer erheblich besseren Gesamtleistung führen, insbesondere wenn der Schleifenkörper einen geringen Arbeitsaufwand pro Element aufweist.In diesem Fall können Sie beide Optionen kombinieren, indem Sie Folgendes schreiben:
Beachten Sie, dass dies auch in dieser kürzeren Form geschrieben werden kann:
quelle
DoSomething
istasync void DoSomething
?async Task DoSomething
?Die erste Version blockiert synchron den aufrufenden Thread (und führt einige der Aufgaben darauf aus).
Wenn es sich um einen UI-Thread handelt, friert dies die UI ein.
Die zweite Version führt die Aufgaben asynchron im Thread-Pool aus und gibt den aufrufenden Thread frei, bis sie fertig sind.
Es gibt auch Unterschiede bei den verwendeten Planungsalgorithmen.
Beachten Sie, dass Ihr zweites Beispiel auf gekürzt werden kann
quelle
await Task.WhenAll(strings.Select(async s => await Task.Run(() => DoSomething(s)));
? Ich hatte Probleme beim Zurückgeben von Aufgaben (anstatt zu warten), insbesondere wenn Anweisungen wieusing
zum Entsorgen von Objekten beteiligt waren.Am Ende tat ich dies, da es sich leichter zu lesen anfühlte:
quelle
Ich habe gesehen, dass Parallel.ForEach unangemessen verwendet wurde, und ich dachte, ein Beispiel in dieser Frage würde helfen.
Wenn Sie den folgenden Code in einer Konsolen-App ausführen, sehen Sie, wie die in Parallel.ForEach ausgeführten Aufgaben den aufrufenden Thread nicht blockieren. Dies kann in Ordnung sein, wenn Sie sich nicht für das Ergebnis interessieren (positiv oder negativ), aber wenn Sie das Ergebnis benötigen, sollten Sie sicherstellen, dass Sie Task.WhenAll verwenden.
Hier ist das Ergebnis:
Fazit:
Wenn Sie Parallel.ForEach mit einer Aufgabe verwenden, wird der aufrufende Thread nicht blockiert. Wenn Sie sich für das Ergebnis interessieren, warten Sie auf die Aufgaben.
~ Prost
quelle