Uruchamianie wielu zadań asynchronicznych i oczekiwanie na ich wykonanie

Muszę uruchomić wiele zadań asynchronicznych w aplikacji konsolowej i poczekać, aż wszystkie zostaną ukończone przed dalszym przetwarzaniem.

Jest wiele artykułów, ale wydaje mi się, że im więcej czytam, tym bardziej się mylę. Przeczytałem i zrozumiałem podstawowe zasady biblioteki zadań, ale najwyraźniej gdzieś brakuje mi linku.

Rozumiem, że możliwe jest łączenie zadań tak, aby zaczynały się po kolejnych zakończeniach (co jest w zasadzie scenariuszem dla wszystkich artykułów, które mam Czytaj), ale chcę, aby wszystkie moje zadania działały w tym samym czasie i chcę wiedzieć, kiedy wszystkie zostaną ukończone.

Jaka jest najprostsza realizacja takiego scenariusza?

Author: Yuval Itzchakov, 2014-07-29

6 answers

Obie odpowiedzi nie wspominały o oczekiwaniu Task.WhenAll:

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

await Task.WhenAll(task1, task2);

Główna różnica między Task.WaitAll i Task.WhenAll jest to, że pierwszy blokuje (podobnie jak użycie Wait na jednym zadaniu), podczas gdy drugi nie będzie i może być oczekiwany, dając kontrolę z powrotem do wywołującego, dopóki wszystkie zadania nie zakończą się.

Bardziej, obsługa wyjątków różni się:

Task.WaitAll:

Co najmniej jedna instancja zadania została anulowana-lub-wyjątek został wyrzucony podczas wykonywania co najmniej jednego z wystąpień zadania. Jeśli zadanie zostało anulowane, obiekt AggregateException zawiera obiekt Operationcanceledexceptions w kolekcji InnerExceptions.

Task.WhenAll:

Jeśli któreś z dostarczonych zadań zakończy się w stanie uszkodzonym, zwrócone zadanie również zakończy się w stanie uszkodzonym, gdzie jego wyjątki będą zawierały agregację zestawu rozpakowanych WYJĄTKÓW z każdego dostarczonego zadania.

Jeśli brak z dostarczonych zadań, które zostały uszkodzone, ale co najmniej jedno z nich zostało anulowane, zwrócone zadanie zakończy się w stanie anulowanym.

Jeśli żadne z zadań nie zostało popełnione i Żadne z zadań nie zostało anulowane, powstałe zadanie zakończy się w stanie RanToCompletion. Jeśli dostarczona tablica / enumerable nie zawiera żadnych zadań, zwrócone zadanie natychmiast przejdzie do stanu RanToCompletion, zanim zostanie zwrócone do wywołującego.

 297
Author: Yuval Itzchakov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-28 11:52:55

Możesz utworzyć wiele zadań takich jak:

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

Task.WaitAll(TaskList.ToArray());
 86
Author: Virus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-19 07:34:44

Najlepszą opcją jaką widziałem jest następująca metoda rozszerzenia:

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

Nazwij to tak:

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

Lub z asynchronicznym lambda:

await sequence.ForEachAsync(async item => {
    var more = await GetMoreAsync(item);
    await more.FrobbleAsync();
});
 19
Author: me22,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-13 20:52:41

Czy chcesz połączyć Task s, czy można je wywoływać równolegle?

Do łączenia
Po prostu zrób coś takiego

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

I nie zapomnij sprawdzić poprzedniej instancji Task w każdej ContinueWith, ponieważ może ona zostać uszkodzona.

Dla sposobu równoległego
Najprostsza metoda, na jaką natknąłem się: Parallel.Invoke W przeciwnym razie jest Task.WaitAll możesz też użyć WaitHandle s do wykonania odliczania do zera (poczekaj, jest nowy Klasa: CountdownEvent), albo ...

 9
Author: Andreas Niedermair,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-29 07:09:16

Możesz użyć WhenAll który zwróci oczekiwany Task lub WaitAll który nie ma typu return i zablokuje dalsze wykonywanie koduThread.Sleep dopóki wszystkie zadania nie zostaną ukończone, anulowane lub usunięte.

Tutaj wpisz opis obrazka

Przykład

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

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

Jeśli chcesz uruchomić zadania w praktycznej kolejności, możesz uzyskać inspirację w formularzu Ten anwser.

 6
Author: NtFreX,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-23 22:28:02

Tak to robię z tablicą 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
Author: DalSoft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:20