コンソールアプリケーションで複数の非同期タスクを実行し、それらがすべて処理される前に完了するのを待つ必要があります。
そこにはたくさんの記事がありますが、私が読むほど混乱するようです。タスクライブラリの基本原則を読んで理解しましたが、どこかにリンクがないのは明らかです。
タスクが次のタスクの完了後に開始されるようにチェーン化することは可能であることを理解しています(これは私が読んだすべての記事のシナリオです)が、すべてのタスクを同時に実行したいです。すべて完了しました。
このようなシナリオの最も簡単な実装は何ですか?
どちらの回答も、待望の Task.WhenAll
については言及していませんでした。
var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();
await Task.WhenAll(task1, task2);
Task.WaitAll
とTask.WhenAll
の主な違いは、前者はブロックし(単一のタスクでWait
を使用するのと同じように)、後者は待つことができず、すべてのタスクが終了するまで呼び出し元に制御を戻すことです。
そのため、例外処理は異なります。
Task.WaitAll
:
少なくとも1つのタスクインスタンスがキャンセルされたか、少なくとも1つのタスクインスタンスの実行中に例外が送出されました。タスクがキャンセルされた場合、AggregateExceptionのInnerExceptionsコレクションにOperationCanceledExceptionが含まれています。
Task.WhenAll
:
提供されたタスクのいずれかが障害状態で完了した場合、返されたタスクも障害状態で完了します。その例外には、各提供タスクからのラップされていない例外のセットの集約が含まれます。
提供されたタスクのどれも失敗しなかったが、それらのうちの少なくとも1つがキャンセルされた場合、返されたタスクはキャンセルされた状態で終了します。
どのタスクも失敗せず、どのタスクも取り消されなかった場合、結果のタスクはRanToCompletion状態で終了します。提供されたarray/enumerableにタスクが含まれていない場合、返されたタスクは呼び出し元に返される前に直ちにRanToCompletion状態に遷移します。
次のような多くのタスクを作成できます。
List<Task> TaskList = new List<Task>();
foreach(...)
{
var LastTask = new Task(SomeFunction);
LastTask.Start();
TaskList.Add(LastTask);
}
Task.WaitAll(TaskList.ToArray());
私が見た中で最良の選択肢は、次の拡張方法です。
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();
});
WhenAll
を使用すると、待機型のTask
または WaitAll
を返すことができ、すべてのタスクが完了、キャンセル、または失敗するまで Thread.Sleep
のようなそれ以上のコード実行をブロックできます。
例
var tasks = new Task[] {
TaskOperationOne(),
TaskOperationTwo()
};
Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);
あなたが特別な順序でタスクを実行したいならば、インスピレーションフォーム これ anwserを得ることができます。
Task
sをチェーニングしますか?それとも並列に呼び出すことができますか?
連鎖用
ちょうど何かをする
Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
また、失敗した可能性があるため、各Task
内の前のContinueWith
インスタンスを確認することを忘れないでください。
並列方式の場合
私が出会った最も簡単な方法は Parallel.Invoke
それ以外の場合は Task.WaitAll
またはゼロまでカウントダウンするためにWaitHandle
sを使用することもできます(待ってください、新しいクラスがあります) : CountdownEvent
)、または...
これは私が配列 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
もう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
}