WaitAllとWhenAll


回答:


502

Task.WaitAll すべてが完了するまで現在のスレッドをブロックします。

Task.WhenAllすべてが完了するまで待機するアクションを表すタスクを返します。

つまり、非同期メソッドから、以下を使用できます。

await Task.WhenAll(tasks);

...つまり、すべてが完了するとメソッドは続行しますが、その時までスレッドを拘束してハングアップすることはありません。


2
ずっと読みになった後、非同期スレッドとは何の関係も持っていないことは明らかであるblog.stephencleary.com/2013/11/there-is-no-thread.html
ヴィンスPanuccio

7
@Vince:「スレッドとは何もしない」は誇張であると思います。非同期操作がスレッドとどのように相互作用するかを理解することが重要です。
Jon Skeet、2015年

6
@KevinBui:いいえ、それはないはずブロック -それはなります、それを待ってから返されたタスクをWhenAll、それはスレッドをブロックと同じではありません。
Jon Skeet

1
@JonSkeetおそらく、これら2つを正確に区別するのは私には微妙すぎます。違いを明確にする参考文献を私(そしておそらく私たちの残りの人)に向けることができますか?
CatShoes、2015

124
@CatShoes:そうでもない-私はすでに説明したのと同じくらいそれを説明した。類推できると思います-テイクアウトを注文してからドアのそばに立ってそれが到着するのを待つのと、テイクアウトを注文して他のことを行い、宅配便が到着したらドアを開けるのとの違いのようです...
ジョンスキート

50

JonSkeetの答えは、通常は優れた方法で違いを説明していますが、別の違いがあります。例外処理です。

Task.WaitAllAggregateExceptionいずれかのタスクがスローしたときにをスローし、スローされたすべての例外を調べることができます。アンラップと「戻り」のみ最初の例外。awaitawait Task.WhenAllAggregateException

以下のプログラムを実行しawait Task.WhenAll(taskArray)て出力すると以下のようになります。

19/11/2016 12:18:37 AM: Task 1 started
19/11/2016 12:18:37 AM: Task 3 started
19/11/2016 12:18:37 AM: Task 2 started
Caught Exception in Main at 19/11/2016 12:18:40 AM: Task 1 throwing at 19/11/2016 12:18:38 AM
Done.

以下のプログラムをTask.WaitAll(taskArray)出力付きで実行すると以下のようになります。

19/11/2016 12:19:29 AM: Task 1 started
19/11/2016 12:19:29 AM: Task 2 started
19/11/2016 12:19:29 AM: Task 3 started
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 1 throwing at 19/11/2016 12:19:30 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 2 throwing at 19/11/2016 12:19:31 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 3 throwing at 19/11/2016 12:19:32 AM
Done.

プログラム:

class MyAmazingProgram
{
    public class CustomException : Exception
    {
        public CustomException(String message) : base(message)
        { }
    }

    static void WaitAndThrow(int id, int waitInMs)
    {
        Console.WriteLine($"{DateTime.UtcNow}: Task {id} started");

        Thread.Sleep(waitInMs);
        throw new CustomException($"Task {id} throwing at {DateTime.UtcNow}");
    }

    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            await MyAmazingMethodAsync();
        }).Wait();

    }

    static async Task MyAmazingMethodAsync()
    {
        try
        {
            Task[] taskArray = { Task.Factory.StartNew(() => WaitAndThrow(1, 1000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(2, 2000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(3, 3000)) };

            Task.WaitAll(taskArray);
            //await Task.WhenAll(taskArray);
            Console.WriteLine("This isn't going to happen");
        }
        catch (AggregateException ex)
        {
            foreach (var inner in ex.InnerExceptions)
            {
                Console.WriteLine($"Caught AggregateException in Main at {DateTime.UtcNow}: " + inner.Message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Caught Exception in Main at {DateTime.UtcNow}: " + ex.Message);
        }
        Console.WriteLine("Done.");
        Console.ReadLine();
    }
}

13
最大の実用的な違いは、例外処理です。本当に?それが実際に最大の違いではないからです。最大の実用的な違いは、1つは非同期で非ブロッキングであり、もう1つはブロッキングです。これは、例外の処理方法よりもはるかに重要です。
Liam

5
これを指摘してくれてありがとう。この説明は、私が現在作業しているシナリオで役立ちました。おそらく「最大の実用的な違い」ではないでしょうが、間違いなく良い呼びかけです。
2018

最大の実用的な差である例外処理は、間の比較により適用されるかもしれませんawait t1; await t2; await t3;VSawait Task.WhenAll(t1,t2,t3);
frostshoxx

1
この例外動作は、ここのドキュメント(docs.microsoft.com/en-us/dotnet/api/…)と矛盾しませんか?「提供されたタスクのいずれかがフォルト状態で完了した場合、返されたタスクもフォルト状態で完了します、その例外には、提供された各タスクからのラップされていない例外のセットの集約が含まれます。」
Dasith Wijes

1
これはのアーティファクトでありawait、2つの方法の違いではないと考えています。どちらもAggregateException、直接またはプロパティ(Task.Exceptionプロパティ)を介してスローするを伝播します。
Theodor Zoulias

20

違いの例として、タスクがある場合、UIスレッドで何かを実行します(たとえば、ストーリーボードのアニメーションを表すタスク)Task.WaitAll()。UIスレッドはブロックされ、UIは更新されません。使用するawait Task.WhenAll()と、UIスレッドはブロックされず、UIが更新されます。


7

彼らは何をしますか:

  • 内部的には両方とも同じことを行います。

違いは何ですか:

  • WaitAllはブロッキング呼び出しです
  • WhenAll - not-コードは実行を継続します

次の場合に使用します。

  • 結果がないと続行できない場合のWaitAll
  • WhenAllいつ通知されるだけで、ブロックされない

1
@MartinRhodesしかし、すぐにそれを待たずに、他の作業を続けてからそれを待つとどうなるでしょうか。WaitAll私が理解しているように、あなたにはその可能性はありません。
Jeppe

@Jeppe 他の作業を行ったTask.WaitAll の呼び出しを変えてみませんか?つまり、タスクを開始した直後に呼び出すのではありません。
PL
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.