複数のタスクの終了待ちをする

複数のタスクの終了待ちをするには await Task.WhenAll または await Task.WhenAny を用います。

await Task.WhenAll は、すべてのタスクが終わるまで終了待ちします.

await Task.WhenAny は、どれかひとつのタスクが終わるまで終了待ちします。

たとえばこんなアプリケーションを作ってみましょう。

Form1 に button10、button11、button20、button21 を配置します。

Form1 に pictureBoxR、pictureBoxG、pictureBoxB を配置します。それぞれの Paint イベントを準備しておきます。

また Form1 に Load のイベントと Paint のイベントを準備しておきます。

button10 は WhenAll に対していちいち複数の引数を与える方法です。
button11 は WhenAll に対していっきに List で引数を与える方法です。

button20 は WhenAny に対していちいち複数の引数を与える方法です。
button21 は WhenAny に対していっきに List で引数を与える方法です。

それぞれの Task で実行するメソッドは、サンプルコードの 204行目の UpdateColor() というメソッドです。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace aaa
{
	public partial class Form1 : Form
	{

		public Form1()
		{
			InitializeComponent();
		}

		const uint FLAG_R = 0x00000004;
		const uint FLAG_G = 0x00000002;
		const uint FLAG_B = 0x00000001;

		uint CounterR = 0;
		uint CounterG = 0;
		uint CounterB = 0;

		private void Form1_Load( object sender, EventArgs e )
		{
			button10.Text = "WhenAll";
			button11.Text = "WhenAll (List)";
			button20.Text = "WhenAny";
			button21.Text = "WhenAny (List)";
		}

		// async をつけてください.
		private async void button10_Click( object sender, EventArgs e )
		{

			Task taskR = Task.Run(() => {
				UpdateColor( FLAG_R, this );
			});

			Task taskG = Task.Run(() => {
				UpdateColor( FLAG_G, this );
			});

			Task taskB = Task.Run(() => {
				UpdateColor( FLAG_B, this );
			});

			// すべてのタスクが完了するのを待つ.
			await Task.WhenAll( taskR, taskG, taskB );

			MessageBox.Show( "すべてのタスクが完了しました." );

		}

		// async をつけてください.
		private async void button11_Click( object sender, EventArgs e )
		{

			Task taskR = Task.Run(() => {
				UpdateColor( FLAG_R, this );
			});

			Task taskG = Task.Run(() => {
				UpdateColor( FLAG_G, this );
			});

			Task taskB = Task.Run(() => {
				UpdateColor( FLAG_B, this );
			});

			// 3つのタスクをリストにする.
			List <Task> tasks = new List<Task>();
			tasks.Add( taskR );
			tasks.Add( taskG );
			tasks.Add( taskB );

			// すべてのタスクが完了するのを待つ.
			await Task.WhenAll( tasks );

			MessageBox.Show( "すべてのタスクが完了しました." );

		}

		// async をつけてください.
		private async void button20_Click( object sender, EventArgs e )
		{

			Task taskR = Task.Run(() => {
				UpdateColor( FLAG_R, this );
			});

			Task taskG = Task.Run(() => {
				UpdateColor( FLAG_G, this );
			});

			Task taskB = Task.Run(() => {
				UpdateColor( FLAG_B, this );
			});

			// どれかひとつのタスクが完了するのを待つ.
			Task the_task = await Task.WhenAny( taskR, taskG, taskB );

			String str_task_name = "";

			if ( the_task.Id == taskR.Id )
			{
				str_task_name = "taskR";
			}
			else if ( the_task.Id == taskG.Id )
			{
				str_task_name = "taskG";
			}
			else if ( the_task.Id == taskB.Id )
			{
				str_task_name = "taskB";
			}

			MessageBox.Show( String.Format( "{0} task is finished.", str_task_name ) );

		}

		// async をつけてください.
		private async void button21_Click( object sender, EventArgs e )
		{

			Task taskR = Task.Run(() => {
				UpdateColor( FLAG_R, this );
			});

			Task taskG = Task.Run(() => {
				UpdateColor( FLAG_G, this );
			});

			Task taskB = Task.Run(() => {
				UpdateColor( FLAG_B, this );
			});


			// 3つのタスクをリストにする.
			List <Task> tasks = new List<Task>();
			tasks.Add( taskR );
			tasks.Add( taskG );
			tasks.Add( taskB );

			// どれかひとつのタスクが完了するのを待つ.
			Task the_task = await Task.WhenAny( tasks );

			String str_task_name = "";

			if ( the_task.Id == taskR.Id )
			{
				str_task_name = "taskR";
			}
			else if ( the_task.Id == taskG.Id )
			{
				str_task_name = "taskG";
			}
			else if ( the_task.Id == taskB.Id )
			{
				str_task_name = "taskB";
			}

			MessageBox.Show( String.Format( "{0} task is finished.", str_task_name ) );

		}

		private void Form1_Paint( object sender, PaintEventArgs e )
		{

			this.Text = String.Format( "RGB({0},{1},{2})", CounterR, CounterG, CounterB );

			pictureBoxR.Invalidate( true );
			pictureBoxG.Invalidate( true );
			pictureBoxB.Invalidate( true );
		}

		private void pictureBoxR_Paint( object sender, PaintEventArgs e )
		{
			byte levelR = (byte)( CounterR );
			byte levelG = 0x00;
			byte levelB = 0x00;
			pictureBoxR.BackColor = Color.FromArgb( levelR, levelG, levelB );
		}

		private void pictureBoxG_Paint( object sender, PaintEventArgs e )
		{
			byte levelR = 0x00;
			byte levelG = (byte)( CounterG );
			byte levelB = 0x00;
			pictureBoxG.BackColor = Color.FromArgb( levelR, levelG, levelB );
		}

		private void pictureBoxB_Paint( object sender, PaintEventArgs e )
		{
			byte levelR = 0x00;
			byte levelG = 0x00;
			byte levelB = (byte)( CounterB );
			pictureBoxB.BackColor = Color.FromArgb( levelR, levelG, levelB );
		}

		// タスクで実行されるメソッド.
		public void UpdateColor( uint R_or_G_or_B, Form f )
		{

			CounterR = 0;
			CounterG = 0;
			CounterB = 0;

			const int LOOP = 255;

			if ( R_or_G_or_B == FLAG_R )
			{
				// ゆっくり赤にする.
				const int WAIT_MSEC = 50;

				for ( int n = 0; n < LOOP; n++ )
				{

					Thread.Sleep( WAIT_MSEC );
					CounterR++;

					this.Invoke((Action)(() =>
					{
						f.Invalidate();
					}));

				}
			}
			else if ( R_or_G_or_B == FLAG_G )
			{

				// そこそこで緑にする.
				const int WAIT_MSEC = 20;

				for ( int n = 0; n < LOOP; n++ )
				{

					Thread.Sleep( WAIT_MSEC );
					CounterG++;

					this.Invoke((Action)(() =>
					{
						f.Invalidate();
					}));

				}
			}
			else if ( R_or_G_or_B == FLAG_B )
			{
				// すばやく青くする.
				const int WAIT_MSEC = 5;

				for ( int n = 0; n < LOOP; n++ )
				{

					Thread.Sleep( WAIT_MSEC );
					CounterB++;

					this.Invoke((Action)(() =>
					{
						f.Invalidate();
					}));

				}
			}
			else
			{
				return;
			}

			return;

		}
	}
}