C#
 Computer >> コンピューター >  >> プログラミング >> C#

【C#】複数の非同期タスクを実行し、すべての完了を待つ方法 ― Task.WaitAllとTask.WhenAllの違い

C#では、複数の非同期タスクを並行して実行し、そのすべてが完了するのを待ちたい場面がよくあります。そんなときに活躍するのが Task.WaitAllTask.WhenAll の2つのメソッドです。どちらも「複数タスクの完了を待つ」という目的で使われますが、動作には重要な違いがあります。

Task.WaitAll と Task.WhenAll の違い

Task.WaitAll は、引数に渡されたすべてのタスクの実行が完了するまで、現在のスレッドをブロック(停止)します。そのため、呼び出し元の後続コードは、全タスクが終了するまで一切実行されません。

一方、Task.WhenAll は「他のすべてのタスクが完了したときに完了する新しいタスク」を作成して返すメソッドです。現在のスレッドをブロックしないため、後続のコードはすぐに実行されます。実際に完了を待ちたい場合は、戻り値のタスクに対して await を付けて呼び出すのが一般的です。

例1:Task.WhenAll を使用した場合

次の例では Task.WhenAll を呼び出していますが、スレッドがブロックされないため、「Both Tasks Completed.」というメッセージが、各タスクの完了メッセージより先に出力されています。これが WhenAll が実行をブロックしないことの証拠です。

static void Main(string[] args){
   Task task1 = new Task(() =>{
      for (var i = 0; i < 5; i++){
         Console.WriteLine("Task 1 - iteration {0}", i);
         Task.Delay(1000);
      }
      Console.WriteLine("Task 1 complete");
   });
   Task task2 = new Task(() =>{
      for (var i = 0; i < 5; i++){
         Console.WriteLine("Task 2 - iteration {0}", i);
         Task.Delay(1000);
      }
      Console.WriteLine("Task 2 complete");
   });
   task1.Start();
   task2.Start();
   Console.WriteLine("Waiting for tasks to complete.");
   Task.WhenAll(task1, task2);
   Console.WriteLine("Both Tasks Completed.");
   Console.ReadLine();
}

出力結果

Waiting for tasks to complete.
Both Tasks Completed.
Task 1 - iteration 0
Task 2 - iteration 0
Task 2 - iteration 1
Task 2 - iteration 2
Task 2 - iteration 3
Task 1 - iteration 1
Task 1 - iteration 2
Task 1 - iteration 3
Task 1 - iteration 4
Task 1 complete
Task 2 - iteration 4
Task 2 complete

なお、このコード内の Task.Delay(1000) は戻り値を待機していないため、実際には遅延として機能していません。そのため各タスクは高速に反復処理を実行し、両タスクの出力が交互に現れています。

例2:Task.WaitAll を使用した場合

次の例では Task.WaitAll を使用しています。今度は「Both Tasks Completed.」が、両方のタスクが完全に終了した後にのみ出力されています。これは WaitAll が実行をブロックすることを示しています。

static void Main(string[] args){
   Task task1 = new Task(() =>{
      for (var i = 0; i < 5; i++){
         Console.WriteLine("Task 1 - iteration {0}", i);
         Task.Delay(1000);
      }
      Console.WriteLine("Task 1 complete");
   });
   Task task2 = new Task(() =>{
      for (var i = 0; i < 5; i++){
         Console.WriteLine("Task 2 - iteration {0}", i);
         Task.Delay(1000);
      }
      Console.WriteLine("Task 2 complete");
   });
   task1.Start();
   task2.Start();
   Console.WriteLine("Waiting for tasks to complete.");
   Task.WaitAll(task1, task2);
   Console.WriteLine("Both Tasks Completed.");
   Console.ReadLine();
}

出力結果

Waiting for tasks to complete.
Task 1 - iteration 0
Task 2 - iteration 0
Task 1 - iteration 1
Task 1 - iteration 2
Task 1 - iteration 3
Task 1 - iteration 4
Task 1 complete
Task 2 - iteration 1
Task 2 - iteration 2
Task 2 - iteration 3
Task 2 - iteration 4
Task 2 complete
Both Tasks Completed

まとめ:使い分けのポイント

  • Task.WaitAll: すべてのタスクが完了するまで現在のスレッドを同期的にブロックする。コンソールアプリなど、ブロックしても問題ない場面向け。
  • Task.WhenAll: スレッドをブロックせず、全タスク完了時に完了するタスクを返す。await Task.WhenAll(...) の形で使うのが基本。

特にGUIアプリケーションやWebアプリケーションでは、WaitAll によるブロックが画面フリーズやスレッドプール枯渇の原因になり得ます。そのため、モダンなC#開発では WhenAll + await の非同期パターンを使用することが強く推奨されます。

  1. Gmailでタスクを作成・管理する方法を徹底解説

    タスクやリマインダーの作成・管理には、従来サードパーティ製ツールがよく使われてきました。各アプリストアには数百種類ものWeb・モバイルアプリが存在し、それぞれに特徴や強みがあります。 しかし、メールクライアントという観点で見ると、タスクの作成と管理において最も優れているのはGmailです。Outlookのタスク機能やMacのApple Mailとの連携など、類似の機能は存在しますが、WebベースのプラットフォームであるGmailはそのどちらをも上回る性能を発揮します。 この記事では、Gmailのタスク機能について詳しく解説し、タスクの作成・管理・活用方法をわかりやすくご紹介します。 Gmail

  2. Windowsでタスクを自動化する方法【タスクスケジューラ活用ガイド】

    ノートパソコンやデスクトップPCは、多くの人にとって欠かせないツールとなっています。仕事をこなすのも、家族や友人との連絡も、一日の終わりにリラックスすることも、Windowsが動作するデバイスならすべてこなせます。こうした環境を常に快適に保つため、Windowsはバックグラウンドで更新プログラムの確認や一時ファイルの削除など、数多くのメンテナンスタスクを自動的に実行しています。これらは「タスクスケジューラ」というツールによって管理されており、このツールは自分で作成したスクリプトでも自由にカスタマイズできます。基本的な使い方としては、特定の条件が満たされたときに特定のアプリを自動的に開くといった