2012-10-03 3 views
5

Wir haben eine Webservice-Funktion, die eine Liste von Aufgaben im Hintergrund ausführt. Wenn alle Aufgaben abgeschlossen sind, wird es zurückgegeben.Rewrite Multi-Thread warten mit async/erwarten

var result = new List<int>(); 
var tasks = new List<Task<int>>(); 

foreach (var row in rowlist) 
    tasks.Add(Task.Factory.StartNew(() => DoWork(row))); 
foreach (var task in tasks) 
    result.Add(task.Result); 

Die task.Result wartet getan für die individuelle Aufgabe zu: Ich habe die Funktion für C# 5. Ich habe die folgende Schnipsel Funktion erhalten verwaltet revisited. Ich habe dies getestet, indem ich der Methode DoWork eine Verzögerung von drei Sekunden hinzugefügt und überprüft habe, dass sie in weniger als 5 Sekunden für eine Liste von 10 Aufgaben abgeschlossen ist.

Jetzt habe ich versucht, die Funktion mit der neuen async/await Syntax neu zu schreiben. Aber ich kann es nicht zur Arbeit bringen. Wenn es kompiliert wird, wird es synchron ausgeführt, was durch Hinzufügen einer Wartezeit bestätigt wird.

Haben Sie Ideen, wie Sie das obige Snippet mit async/await umschreiben können?

Antwort

9

Ich würde schreiben dies als:

var tasks = rowlist.Select(row => Task.Run(() => DoWork(row))); 

var results = (await Task.WhenAll(tasks)).ToList(); 

Grundsätzlich Sie noch die Aufgaben, die die Art und Weise erstellen Sie vorher waren (ohne await zu verwenden), wie Sie sie alle sofort gestartet werden soll. Sie können dann darauf warten, dass alle asynchron ausgeführt werden (über await Task.WhenAll), und dann die Ergebnisse sofort herausziehen (da Sie wissen, dass sie alle erledigt sind).

+0

Dies läuft async. Aber es resultiert eine 'System.AggregateException', innere Ausnahme' {"Die Abfrageergebnisse können nicht mehr als einmal aufgelistet werden."} 'Irgendeine Idee? – Andomar

+0

Vielleicht noch wichtiger: Warum läuft das synchron, wenn Sie die Zeile mit 'await' weglassen? – Andomar

+1

@Andomar Rufen Sie 'ToList' nach' Select' auf, wenn Sie die Aufgaben erstellen. Das sollte den Fehler beheben. Derzeit würde es beim wiederholten Aufzählen von "Aufgaben" versuchen, die neuen Aufgaben zweimal zu erstellen, was schlecht wäre. – Servy

2

Basierend auf Reed Copsey, Servy und Stephen Cleary Antwort und Kommentare, die ich zu dieser Lösung fortgeschritten haben:

var results = rowlist 
    .Select(row => Task.Run(() => DoWork(row))) 
    .ToList() 
    .Select(t => t.Result); 

Die zweite Select auf die Aufgaben warten zu beenden. Ich habe eine ToList() dazwischen hinzugefügt, um das Streaming zu verhindern, aber es scheint ohne zu funktionieren.

+1

Dies entspricht im Wesentlichen dem Code, mit dem Sie begonnen haben. Du brauchst das 'erwarten' dort ... – Servy

+0

@Servy: Jetzt bin ich verwirrt: Warst du es nicht, der oben gesagt hat, dass' task.Result' auch blockieren kann? Der Code funktioniert übrigens: 10 Aufgaben in 4 Sekunden, wobei jede Aufgabe 3 Sekunden lang schläft. – Andomar

+2

Der ganze Sinn der Verwendung von async/await ist, dass die gesamte Methode (in der sich dieser Code befindet) nicht blockiert wird.Sie können die Methoden beispielsweise von einem Ereignis in einer GUI-Anwendung aufrufen und nicht den UI-Thread blockieren (während der gesamte benötigte Code im UI-Thread ausgeführt wird). Mit 'erwarten' gibt die Methode die Ergebnisse nicht zurück, sondern gibt eine Aufgabe zurück, die Sie darüber informiert, wann diese Ergebnisse berechnet wurden (und möglicherweise, was diese Ergebnisse waren). – Servy