2012-04-06 9 views
48

beginne ich ein paar parallele Aufgaben, wie folgt aus:Gibt es eine generische Task.WaitAll?

var tasks = 
    Enumerable.Range(1, 500) 
    .Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue)) 
    .ToArray(); 

und dann kommen Sie mit

Task.WaitAll(tasks); 

Auf dieser letzten Zeile ich eine tasks unter verschnörkelten Marker blau bekommen, mit einer Warnmeldung:

Co-variant array conversion from Task[] to Task[] 
can cause run-time exception on write operation.

Ich verstehe, warum ich diese Botschaft bekomme, aber gibt es einen Weg dahin? (Zum Beispiel, wie eine generische Version von Task.WaitAll()?)

+1

In diesem Fall ist die Konvertierung sicher, weil 'WaitAll()' nicht in das Array schreiben wird. Gibt es einen Grund, warum Sie es vermeiden wollen? – svick

+11

.Net 4.5 enthält auch ['Task.WhenAll()'] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.whenall%28v=vs.110% 29.aspx), die eine einzelne "Aufgabe" zurückgibt, die abgeschlossen wird, wenn alle Aufgaben in der Sammlung abgeschlossen sind. Und es hat auch eine generische Version und es funktioniert auf jedem 'IEnumerable ' von 'Task's. – svick

+0

@svick thx für den Tipp. sieht so aus, als hätten sie das, worüber du sprichst, in WhenAll umbenannt, also kannst du einfach 'appear task' sagen.WhenAll (Aufgabe1, Aufgabe2); ' –

Antwort

8

Sie eine Verlängerung Methode, dies zu tun zu schaffen.

Ich weiß nicht, die genaue Umsetzung von WaitAll, aber wir können davon ausgehen, es wartet auf jedes Element zu vervollständigen:

static class TaskExtensions 
{ 
    public static void WaitAll<T>(this Task<T>[] tasks) 
    { 
     foreach (var item in tasks) 
     { 
      item.Wait(); 
     } 
    } 
} 

Dann rufen, von Ihrem aktuellen Code:

tasks.WaitAll(); 

bearbeiten

Die tatsächliche Implementierung ist ein bisschen komplexer. Ich habe den Code aus dieser Antwort weggelassen, weil er ziemlich lang ist.

http://pastebin.com/u30PmrdS

Sie können dies ändern, allgemeine Aufgaben zu unterstützen.

+0

Die "tatsächliche Implementierung" ist ziemlich nicht trivial. Nicht zu vergessen, Sie verlieren (oder müssen neu codieren), wenn die .NET Framework-Implementierung Fehlerbehebungen oder Leistungsverbesserungen aufweist. Die Lösungen von @MerickOWA und DMac the Destroyer (in dieser Reihenfolge) lösen das OP-Problem sehr einfach. – Mike

+0

Das ist wirklich offensichtlich falsch - Sie führen Ihre Aufgaben * seriell * hier, was nicht das ist, was die echte 'WaitAll' tut. –

+0

@MarkAmery Dies setzt voraus, dass die Aufgaben bereits gesendet werden, wenn sie in diese Methode eingegeben werden. – Bas

21

Eine generische Methode von Task.WaitAll würde bedeuten, dass alle Aufgaben den gleichen Typ zurückgeben müssten, was äußerst begrenzte Nützlichkeit wäre. So etwas zu schreiben, könnte manuell gemacht werden (siehe Bas Brekelmans Antwort), aber das erlaubt ContinueWith oder Abbruch nicht ohne viel Arbeit.

Eine einfache Lösung, wenn Sie für etwas anderes nicht mit dem Array ist

.ToArray<Task>(); 
+0

+1, das ist eine Möglichkeit, aber in meinem Fall verwende ich das Array tatsächlich, um die 'int' Ergebnisse zu erhalten:' var results = tasks.Select (task => task.Result) ' – GolfWolf

+1

@ W0lf dann tun Sie einfach ein Extra. ToArray () bevor Sie es an Task.WaitAll übergeben, kann eine mögliche Zuordnung zu diesem kopierten Task [] keine Laufzeitausnahme verursachen – MerickOWA

20

Ich bin mir ziemlich sicher, dass es ein sicherer Betrieb ist auch mit der Warnung, aber wenn Sie wirklich um eine bessere Option bekommen wollen, als Ihre eigene Implementierung zu schaffen einfach wäre Ihre tasks Parameter in die Art konvertieren sie will:

Task.WaitAll(tasks.Cast<Task>().ToArray()) 

, dass die blaue Kringel für mich tötet, lässt mich meine tasks Variable generic halten und zwingt mich nicht eine ganze Menge neuer beängstigend Code zu erstellen, die letztlich nicht notwendig ist.

+1

Das funktionierte perfekt für mich. Getestet sowohl mit Tasks, die bis zum Abschluss ausgeführt wurden, als auch für Tasks, bei denen das Zeitlimit überschritten wurde und 'var ifAllTasksRanToCompletion = Task.WaitAll (tasks.Cast (). ToArray(), timeout: TimeSpan.FromSeconds (20));'. – Contango