it-swarm.com.de

Mehrere asynchrone Aufgaben ausführen und darauf warten, dass alle abgeschlossen sind

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

Es gibt viele Artikel, aber je mehr ich lese, desto verwirrter wird es. Ich habe die Grundprinzipien der Aufgabenbibliothek gelesen und verstanden, aber irgendwo fehlt mir eindeutig ein Link.

Ich verstehe, dass es möglich ist, Aufgaben so zu verketten, dass sie nach dem Abschluss einer anderen Aufgabe gestartet werden (was so ziemlich das Szenario für alle Artikel ist, die ich gelesen habe), aber ich möchte, dass alle meine Aufgaben gleichzeitig ausgeführt werden, und ich möchte es einmal wissen Sie sind alle fertig.

Was ist die einfachste Implementierung für ein solches Szenario?

227
Daniel Minnaar

In beiden Antworten wurde das erwartbare Task.WhenAll nicht erwähnt:

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

await Task.WhenAll(task1, task2);
_

Der Hauptunterschied zwischen Task.WaitAll und _Task.WhenAll_ besteht darin, dass ersterer blockiert (ähnlich wie bei der Verwendung von Wait für eine einzelne Aufgabe), während letzterer wird und kann nicht abgewartet werden und gibt dem Anrufer die Kontrolle zurück, bis alle Aufgaben beendet sind.

Ausnahmebehandlung unterscheidet sich:

Task.WaitAll:

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

Task.WhenAll:

Wenn eine der bereitgestellten Aufgaben in einem fehlerhaften Zustand abgeschlossen wird, wird die zurückgegebene Aufgabe auch in einem fehlerhaften Zustand abgeschlossen, in dem ihre Ausnahmen die Aggregation des Satzes nicht umschlossener Ausnahmen von jeder der bereitgestellten Aufgaben enthalten.

Wenn keine der gelieferten Aufgaben fehlerhaft ist, aber mindestens eine abgebrochen wurde, endet die zurückgegebene Aufgabe im Status Abgebrochen.

Wenn keine der Aufgaben fehlgeschlagen ist und keine der Aufgaben abgebrochen wurde, endet die resultierende Aufgabe im Status RanToCompletion. Wenn das angegebene Array/Enumerable keine Aufgaben enthält, wechselt die zurückgegebene Aufgabe sofort in einen RanToCompletion-Status, bevor sie an den Aufrufer zurückgegeben wird.

375
Yuval Itzchakov

Sie könnten viele Aufgaben erstellen wie:

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

Task.WaitAll(TaskList.ToArray());
95
Virus

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

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

Nenne es so:

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

Oder mit einem asynchronen Lambda:

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

Sie können WhenAll verwenden, das einen erwarteten Task zurückgibt, oder WaitAll , das keinen Rückgabetyp hat und die weitere Codeausführung blockiert, ähnlich wie Thread.Sleep = bis alle Aufgaben erledigt, abgebrochen oder fehlerhaft sind.

enter image description here

Beispiel

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

Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);
_

Wenn Sie die Aufgaben in einer bestimmten Reihenfolge ausführen möchten, können Sie sich von this anwser inspirieren lassen.

21
NtFreX

Möchten Sie die Tasks verketten, oder können sie parallel aufgerufen werden?

Zum Verketten
Mach einfach so etwas wie

_Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
_

und vergessen Sie nicht, die vorherige Task-Instanz in jeder ContinueWith-Instanz zu überprüfen, da sie möglicherweise fehlerhaft ist.

Für die parallele Art und Weise
Die einfachste Methode, auf die ich gestoßen bin: Parallel.Invoke Andernfalls gibt es Task.WaitAll oder Sie können sogar WaitHandles verwenden, um einen Countdown zu machen Keine Aktionen mehr (warten, es gibt eine neue Klasse: CountdownEvent ) oder ...

8

Noch eine andere Antwort ... aber normalerweise finde ich mich in einem Fall wieder, in dem ich Daten gleichzeitig laden und in Variablen einfügen muss, wie zum Beispiel:

var cats = new List<Cat>();
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)
{
    // handle exception
}
4

So mache ich das mit einem Array Func <>:

var tasks = new Func<Task>[]
{
   () => myAsyncWork1(),
   () => myAsyncWork2(),
   () => myAsyncWork3()
};

await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async    
Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync
4
DalSoft