web-dev-qa-db-ja.com

複数の非同期タスクを実行し、それらがすべて完了するのを待つ

コンソールアプリケーションで複数の非同期タスクを実行し、それらがすべて処理される前に完了するのを待つ必要があります。

そこにはたくさんの記事がありますが、私が読むほど混乱するようです。タスクライブラリの基本原則を読んで理解しましたが、どこかにリンクがないのは明らかです。

タスクが次のタスクの完了後に開始されるようにチェーン化することは可能であることを理解しています(これは私が読んだすべての記事のシナリオです)が、すべてのタスクを同時に実行したいです。すべて完了しました。

このようなシナリオの最も簡単な実装は何ですか?

214
Daniel Minnaar

どちらの回答も、待望の Task.WhenAll については言及していませんでした。

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

await Task.WhenAll(task1, task2);

Task.WaitAllTask.WhenAllの主な違いは、前者はブロックし(単一のタスクでWaitを使用するのと同じように)、後者は待つことができず、すべてのタスクが終了するまで呼び出し元に制御を戻すことです。

そのため、例外処理は異なります。

Task.WaitAll

少なくとも1つのタスクインスタンスがキャンセルされたか、少なくとも1つのタスクインスタンスの実行中に例外が送出されました。タスクがキャンセルされた場合、AggregateExceptionのInnerExceptionsコレクションにOperationCanceledExceptionが含まれています。

Task.WhenAll

提供されたタスクのいずれかが障害状態で完了した場合、返されたタスクも障害状態で完了します。その例外には、各提供タスクからのラップされていない例外のセットの集約が含まれます。

提供されたタスクのどれも失敗しなかったが、それらのうちの少なくとも1つがキャンセルされた場合、返されたタスクはキャンセルされた状態で終了します。

どのタスクも失敗せず、どのタスクも取り消されなかった場合、結果のタスクはRanToCompletion状態で終了します。提供されたarray/enumerableにタスクが含まれていない場合、返されたタスクは呼び出し元に返される前に直ちにRanToCompletion状態に遷移します。

359
Yuval Itzchakov

次のような多くのタスクを作成できます。

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

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

私が見た中で最良の選択肢は、次の拡張方法です。

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

このようにそれを呼ぶ:

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

非同期のラムダの場合

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

WhenAll を使用すると、待機型のTaskまたは WaitAll を返すことができ、すべてのタスクが完了、キャンセル、または失敗するまで Thread.Sleep のようなそれ以上のコード実行をブロックできます。

enter image description here

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

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

あなたが特別な順序でタスクを実行したいならば、インスピレーションフォーム これ anwserを得ることができます。

15
NtFreX

Tasksをチェーニングしますか?それとも並列に呼び出すことができますか?

連鎖用
ちょうど何かをする

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

また、失敗した可能性があるため、各Task内の前のContinueWithインスタンスを確認することを忘れないでください。

並列方式の場合
私が出会った最も簡単な方法は Parallel.Invoke それ以外の場合は Task.WaitAll またはゼロまでカウントダウンするためにWaitHandlesを使用することもできます(待ってください、新しいクラスがあります) : CountdownEvent )、または...

8

これは私が配列 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

もう1つの答え... ...しかし、データを同時にロードしてそれを変数に入れる必要がある場合、私は通常自分自身がケースにいることに気付きます。

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
}
3