401 Stimmen

Das Ausführen mehrerer asynchroner Aufgaben und das Warten darauf, dass diese alle abgeschlossen sind

Ich muss mehrere asynchrone Aufgaben in einer Konsolenanwendung ausführen und warten, bis sie alle abgeschlossen sind, bevor ich weiter verarbeite.

Es gibt viele Artikel dazu, aber je mehr ich lese, desto verwirrter werde ich. Ich habe die grundlegenden Prinzipien der Task-Bibliothek gelesen und verstanden, aber offensichtlich fehlt mir irgendwo eine Verbindung.

Ich verstehe, dass es möglich ist, Aufgaben zu verketten, so dass sie nach dem Abschluss einer anderen beginnen (was im Grunde das Szenario für alle Artikel ist, die ich gelesen habe), aber ich möchte alle meine Aufgaben gleichzeitig ausführen lassen und ich möchte wissen, wenn sie alle abgeschlossen sind.

Was ist die einfachste Umsetzung für ein Szenario wie dieses?

610voto

Yuval Itzchakov Punkte 144903

Beide Antworten haben das ansehbare Task.WhenAll nicht erwähnt:

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);

Der Hauptunterschied zwischen Task.WaitAll und Task.WhenAll ist, dass ersteres blockiert (ähnlich wie bei Verwendung von Wait bei einer einzelnen Aufgabe), während letzterer nicht blockiert und abgewartet werden kann, wodurch die Kontrolle an den Aufrufer zurückgegeben wird, bis alle Aufgaben abgeschlossen sind.

Zudem unterscheidet sich die Ausnahmebehandlung:

Task.WaitAll:

Mindestens eine der Aufgabeinstanzen wurde abgebrochen - oder - während der Ausführung mindestens einer der Aufgabeinstanzen wurde eine Ausnahme ausgelöst. Wenn eine Aufgabe abgebrochen wurde, enthält die AggregateException einen OperationCanceledException in ihrer InnerExceptions-Sammlung.

Task.WhenAll:

Wenn eine der bereitgestellten Aufgaben in einem fehlerhaften Zustand abschließt, wird die zurückgegebene Aufgabe ebenfalls in einem fehlerhaften Zustand abschließen, wobei ihre Ausnahmen die Aggregation der Gruppe von entpackten Ausnahmen aus jeder der bereitgestellten Aufgaben enthalten werden.

Wenn keine der bereitgestellten Aufgaben fehlerhaft war, aber mindestens eine von ihnen abgebrochen wurde, wird die zurückgegebene Aufgabe im abgebrochenen Zustand enden.

Wenn keine der Aufgaben fehlerhaft war und keine der Aufgaben abgebrochen wurde, wird die resultierende Aufgabe im Zustand RanToCompletion enden. Wenn das bereitgestellte Array/Aufzählbare keine Aufgaben enthält, wird die zurückgegebene Aufgabe sofort in einen Zustand RanToCompletion übergehen, bevor sie an den Aufrufer zurückgegeben wird.

139voto

Virus Punkte 2407

Sie könnten viele Aufgaben erstellen, wie zum Beispiel:

List TaskList = new List();
foreach(...)
{
   var LastTask = new Task(SomeFunction);
   LastTask.Start();
   TaskList.Add(LastTask);
}

Task.WaitAll(TaskList.ToArray());

70voto

NtFreX Punkte 8807

Sie können WhenAll verwenden, das ein Task zurückgibt, auf das gewartet werden kann, oder WaitAll, das keinen Rückgabetyp hat und die Weiterführung des Codes blockiert, ähnlich wie Thread.Sleep, bis alle Aufgaben abgeschlossen, abgebrochen oder fehlerhaft sind.

WhenAll

WaitAll

Eine der bereitgestellten Aufgaben wird im fehlerhaften Zustand abgeschlossen

Es wird eine Aufgabe mit dem fehlerhaften Zustand zurückgegeben. Die Ausnahmen enthalten die Aggregation der Menge der entpackten Ausnahmen aus jeder der bereitgestellten Aufgaben.

Es wird eine AggregateException ausgelöst.

Keine der bereitgestellten Aufgaben ist fehlerhaft, aber mindestens eine wurde abgebrochen

Die zurückgegebene Aufgabe endet im Zustand TaskStatus.Canceled

Es wird eine AggregateException ausgelöst, die eine OperationCanceledException in ihrer InnerExceptions-Sammlung enthält

Es wurde eine leere Liste übergeben

Es wird eine ArgumentException ausgelöst

Die zurückgegebene Aufgabe wird sofort in einen TaskStatus.RanToCompletion-Zustand übergehen, bevor sie an den Aufrufer zurückgegeben wird.

Blockiert den aktuellen Thread nicht

Blockiert den aktuellen Thread

Beispiel

var tasks = new Task[] {
    TaskOperationOne(),
    TaskOperationTwo()
};

Task.WaitAll(tasks);
// oder
await Task.WhenAll(tasks);

Wenn Sie die Aufgaben in einer bestimmten/reihenfolge ausführen möchten, können Sie sich von dieser Antwort inspirieren lassen.

32voto

me22 Punkte 321

Die beste Option, die ich gesehen habe, ist die folgende Erweiterungsmethode:

public static Task ForEachAsync(this IEnumerable sequence, Func action) {
    return Task.WhenAll(sequence.Select(action));
}

Rufen Sie es wie folgt auf:

await sequence.ForEachAsync(item => item.SomethingAsync(blah));

Oder mit einem async lambda:

await sequence.ForEachAsync(async item => {
    var more = await GetMoreAsync(item);
    await more.FrobbleAsync();
});

16voto

Yehor Hromadskyi Punkte 3180

Noch eine Antwort...aber ich finde mich normalerweise in einem Fall wieder, wenn ich Daten gleichzeitig laden und in Variablen speichern muss, wie:

var cats = new List();
var dog = new Dog();

var loadDataTasks = new Task[]
{
    Task.Run(async () => cats = await LoadCatsAsync()),
    Task.Run(async () => dog = await LoadDogAsync())
};

try
{
    await Task.WhenAll(loadDataTasks);
}
catch (Exception ex)
{
    // Ausnahme behandeln
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X