2013-02-28 18 views
52

Ich würde gerne eine Sammlung parallel behandeln, aber ich habe Probleme bei der Umsetzung und ich hoffe daher auf etwas Hilfe.Parallele foreach mit asynchronem Lambda

Der Fehler tritt auf, wenn ich eine Methode aufrufen möchte, die in C# async innerhalb des Lambda der parallelen Schleife markiert ist. Zum Beispiel:

var bag = new ConcurrentBag<object>(); 
Parallel.ForEach(myCollection, async item => 
{ 
    // some pre stuff 
    var response = await GetData(item); 
    bag.Add(response); 
    // some post stuff 
} 
var count = bag.Count; 

Das Problem tritt bei der Zählung 0 ist, weil alle erstellten Threads effektiv nur Hintergrund-Threads sind und der Parallel.ForEach Anruf wartet nicht für die Fertigstellung. Wenn ich das Asynchron-Schlüsselwort zu entfernen, sieht das Verfahren wie folgt aus:

var bag = new ConcurrentBag<object>(); 
Parallel.ForEach(myCollection, item => 
{ 
    // some pre stuff 
    var responseTask = await GetData(item); 
    responseTask.Wait(); 
    var response = responseTask.Result; 
    bag.Add(response); 
    // some post stuff 
} 
var count = bag.Count; 

Es funktioniert, aber es vollständig deaktiviert erwarten die Klugheit, und ich habe einige manuelle Ausnahmebehandlung zu tun .. (der Kürze halber entfernt).

Wie kann ich eine Parallel.ForEach Schleife implementieren, die das Schlüsselwort wait innerhalb des Lambda verwendet? Ist es möglich?

Der Prototyp der Parallel.ForEach-Methode benötigt einen Action<T> als Parameter, aber ich möchte, dass er auf mein asynchrones Lambda wartet.

+0

Ich nehme an, Sie bedeuten 'await' zu entfernen' await GetData (Po) 'in Ihrem zweiten Codeblock als es einen Kompilierungsfehler als würde produzieren -ist. –

+0

Mögliche Duplikate von [Nesting erwarten in Parallel.ForEach] (https://stackoverflow.com/questions/11564506/nesting-await-in-parallel-foreach) –

Antwort

76

Wenn Sie nur einfache Parallelität möchten, können Sie dies tun:

var bag = new ConcurrentBag<object>(); 
var tasks = myCollection.Select(async item => 
{ 
    // some pre stuff 
    var response = await GetData(item); 
    bag.Add(response); 
    // some post stuff 
}); 
await Task.WhenAll(tasks); 
var count = bag.Count; 

Wenn Sie etwas komplexere benötigen, Stephen Toub's ForEachAsync post überprüfen.

+18

Wahrscheinlich ist ein Drosselmechanismus erforderlich. Dadurch werden sofort so viele Aufgaben erstellt, wie es Elemente gibt, die möglicherweise in 10k-Netzwerkanforderungen enden. – usr

+4

@usr Das letzte Beispiel in Stephen Toubs Artikel spricht das an. – svick

+0

@svick Ich rätselte über die letzte Probe. Es sieht für mich so aus, als würde es eine Ladung Aufgaben auf einmal erledigen, um mehr Aufgaben für mich zu schaffen, aber alle beginnen mit Massenaktionen. –

14

Sie können die ParallelForEachAsync Erweiterungsmethode von AsyncEnumerator NuGet Package:

using System.Collections.Async; 

var bag = new ConcurrentBag<object>(); 
await myCollection.ParallelForEachAsync(async item => 
{ 
    // some pre stuff 
    var response = await GetData(item); 
    bag.Add(response); 
    // some post stuff 
}, maxDegreeOfParallelism: 10); 
var count = bag.Count; 
+0

Das ist Ihr Paket? Ich habe gesehen, dass du das jetzt an einigen Stellen postest? : D Oh warte .. dein Name ist auf dem Paket: D +1 – ppumkin

+5

@ppumkin, ja, es ist meins. Ich habe dieses Problem immer und immer wieder gesehen, also beschlossen, es auf die einfachste Weise zu lösen und andere davon zu befreien, auch zu kämpfen :) –

+0

Danke .. es macht definitiv Sinn und half mir aus großer Zeit! – ppumkin

Verwandte Themen