web-dev-qa-db-ja.com

一般的なTask.WaitAllはありますか?

次のようないくつかの並列タスクを開始します。

_var tasks =
    Enumerable.Range(1, 500)
    .Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue))
    .ToArray();
_

そしてそれらに参加する

_Task.WaitAll(tasks);
_

この最後の行で、tasksの下に青い波線マーカーが表示され、警告メッセージが表示されます。

Task []からTask [] 
への共変配列変換は、書き込み操作でランタイム例外を引き起こす可能性があります。

このメッセージが表示される理由は理解していますが、回避策はありますか? (たとえば、Task.WaitAll()?の汎用バージョンのように)

60

警告が出ても安全な操作だと思いますが、実際に回避したい場合は、独自の実装を作成するよりもtasksパラメータを必要なタイプに変換するだけです。

Task.WaitAll(tasks.Cast<Task>().ToArray())

これにより、青い波線が解消され、tasks変数をジェネリックに保つことができ、最終的に不要なまったく怖いコードをたくさん作成する必要がなくなります。

28

Task.WaitAllのジェネリックメソッドは、すべてのタスクが同じタイプを返さなければならないことを意味します。そのような何かを書くことは手動で行うことができますが(Bas Brekelmansの回答を参照)、これは多くの作業なしでContinueWithまたはキャンセルを許可しません。

他の目的で配列を使用していない場合の簡単な解決策は、

  .ToArray<Task>();
27
MerickOWA

これを行う拡張メソッドを作成できます。

WaitAllの正確な実装はわかりませんが、すべてのアイテムが完了するのを待つと想定できます。

static class TaskExtensions
{
    public static void WaitAll<T>(this Task<T>[] tasks)
    {
        foreach (var item in tasks)
        {
            item.Wait();
        }
    }
}

次に、現在のコードから呼び出します。

tasks.WaitAll();

編集

実際の実装はもう少し複雑です。かなり長いので、この回答からコードを省略しました。

http://Pastebin.com/u30PmrdS

これを変更して、一般的なタスクをサポートできます。

7
Bas

より良い、よりシンプルな答え

実際にあります[〜#〜] is [〜#〜]同様の一般的なオーバーロード:

_Task all = Task.WhenAll(tasks)
_

これは、すべてのタスクが完了した後に完了するTaskを返すという点で異なります。そのため、awaitを使用したり、Wait()を使用したりできます。

署名を見てください:

オーバーロード

---------非一般的なオーバーロード--------------

WhenAll(IEnumerable<Task>)列挙可能なコレクション内のTaskオブジェクトがすべて完了したときに完了するタスクを作成します。

WhenAll(Task[])配列内のすべてのTaskオブジェクトが完了したときに完了するタスクを作成します。

--------- GENERIC OVERLOADS --------------

WhenAll<TResult>(IEnumerable<Task<TResult>>)列挙可能なコレクション内のすべての_Task<TResult>_オブジェクトが完了したときに完了するタスクを作成します。

WhenAll<TResult>(Task<TResult>[])配列内のすべての_Task<TResult>_オブジェクトが完了したときに完了するタスクを作成します。

3
Yitzchak