Nisten warten in Parallel.ForEach
In eine metro-app, die ich ausführen müssen eine Reihe von WCF-Aufrufe. Es gibt eine beträchtliche Anzahl von aufrufen gestellt werden, also muss ich Sie in einer parallelen Schleife. Das problem ist, dass der parallelen Schleife beendet, bevor die WCF-Aufrufe sind alle komplett.
Wie würden Sie umgestalten, dies funktioniert wie erwartet?
var ids = new List<string>() { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
var customers = new System.Collections.Concurrent.BlockingCollection<Customer>();
Parallel.ForEach(ids, async i =>
{
ICustomerRepo repo = new CustomerRepo();
var cust = await repo.GetCustomer(i);
customers.Add(cust);
});
foreach ( var customer in customers )
{
Console.WriteLine(customer.ID);
}
Console.ReadKey();
InformationsquelleAutor der Frage Darthg8r | 2012-07-19
Du musst angemeldet sein, um einen Kommentar abzugeben.
Die ganze Idee hinter
Parallel.ForEach()
ist, dass Sie haben eine Reihe von threads, und jeder thread bearbeitet einen Teil der Sammlung. Als Sie bemerkte, das funktioniert nicht mitasync
-await
, wo Sie wollen, lassen Sie die Gewinde für die Dauer der async-Aufruf.Könnte man "fix", dass durch die Blockierung der
ForEach()
threads, aber das Niederlagen der springende Punkt, derasync
-await
.Was Sie tun können, ist die Verwendung TPL Dataflow statt
Parallel.ForEach()
unterstützt asynchroneTask
s gut.Insbesondere Ihren code geschrieben werden kann, mit einem
TransformBlock
, verwandelt jede id in einCustomer
mit derasync
lambda. Dieser block kann so konfiguriert werden, parallel ausgeführt werden. Sie würde link-block auf eineActionBlock
schreibt jederCustomer
auf der Konsole.Nach einrichten der sperren-Netzwerk können Sie
Post()
jede id, um dieTransformBlock
.Code:
Obwohl Sie wahrscheinlich wollen für die Begrenzung der Parallelität der
TransformBlock
auf einige kleine Konstante. Außerdem könnten Sie begrenzen die Kapazität derTransformBlock
und fügen Sie die Einträge asynchron mitSendAsync()
zum Beispiel, wenn die Sammlung zu groß ist.Als zusätzlichen Vorteil, wenn im Vergleich zu deinem code (wenn es funktionierte) ist, dass der Schreibvorgang wird gestartet, sobald ein einzelnes Element fertig ist, und nicht warten, bis die Verarbeitung abgeschlossen ist.
InformationsquelleAutor der Antwort svick
svick Antwort ist (wie gewohnt) hervorragend.
Allerdings finde ich, Datenfluss, um nützlicher zu sein, wenn man tatsächlich große Mengen an Daten übertragen werden. Oder wenn Sie brauchen eine
async
-kompatibel Warteschlange.In Ihrem Fall eine einfachere Lösung besteht darin, nur die
async
-Stil Parallelität:InformationsquelleAutor der Antwort Stephen Cleary
Mit DataFlow als svick vorgeschlagen zuviel, und Stephen ' s Antwort nicht liefern die Mittel zur Steuerung der Parallelität der operation. Das kann jedoch erreicht werden, eher einfach:
Den
ToArray()
- Aufrufe optimiert werden kann, mit einem array statt einer Liste und ersetzt abgeschlossene Aufgaben, aber ich bezweifle es würde viel von einem Unterschied in den meisten Szenarien. Beispiel für die Nutzung pro die OP ' s Frage:BEARBEITEN Kerl SO Benutzer-und TPL-wiz Eli Arbel wies mich auf eine Verwandte Artikel von Stephen Toub. Wie gewohnt, seine Umsetzung ist sowohl elegant und effizient:
InformationsquelleAutor der Antwort Ohad Schneider
Können Sie sparen Aufwand mit der neuen AsyncEnumerator NuGet-Paket, die nicht existieren vor 4 Jahren, als die Frage war Zitat. Es ermöglicht Ihnen, zu Steuern, den Grad der Parallelität:
Disclaimer: ich bin der Autor des AsyncEnumerator Bibliothek ist open source und lizenziert unter der MIT, und ich bin Entsendung dieser Nachricht nur um der community zu helfen.
InformationsquelleAutor der Antwort Serge Semenov
Wickeln Sie die Parallele.Foreach in eine Aufgabe.Run() und statt dem await-Schlüsselwort verwenden [yourasyncmethod].Ergebnis
(Sie tun müssen, um die Aufgabe.Ausführen Sache nicht blockieren der UI-thread)
Etwas wie dieses:
InformationsquelleAutor der Antwort ofcoursedude
Dieser sollte ziemlich effizient und einfacher als immer die ganze TPL Dataflow arbeiten:
InformationsquelleAutor der Antwort John Gietzen
Nach Einführung einer Reihe von helper-Methoden, werden Sie in der Lage sein, parallel-Abfragen mit diesem einfachen Syntax:
Was hier passiert, ist, dass wir split-source-Sammlung in 10 Blöcken (
.Split(DegreeOfParallelism)
), dann laufen Sie 10 Aufgaben, jede Bearbeitung seiner Elemente nacheinander (.SelectManyAsync(...)
) und ordnen diese wieder in einer Liste angezeigt.Erwähnenswert, es ist ein einfacher Ansatz:
Aber es braucht eine vorsorge: wenn Sie eine source-Sammlung, die zu groß ist, wird es chedule eine
Task
für jedes Element gleich, das kann zu erheblichen Leistungseinbußen.Erweiterung Methoden wie in den Beispielen oben wie folgt Aussehen:
InformationsquelleAutor der Antwort Vitaliy Ulantikov
Ich bin ein wenig spät zur party, aber möchten Sie vielleicht zu prüfen, mit GetAwaiter.GetResult() zum ausführen von asynchronen code im sync-Kontext, sondern als paralled als unten;
InformationsquelleAutor der Antwort Teoman shipahi
Erweiterung Methode was SemaphoreSlim und auch erlaubt, um maximale Grad der Parallelität
Beispiel Für Die Nutzung:
InformationsquelleAutor der Antwort Jay Shah