複数のタスクの終了待ちをする
複数のタスクの終了待ちをするには await Task.WhenAll または await Task.WhenAny を用います。
await Task.WhenAll は、すべてのタスクが終わるまで終了待ちします.
await Task.WhenAny は、どれかひとつのタスクが終わるまで終了待ちします。
たとえばこんなアプリケーションを作ってみましょう。
Form1 に button10、button11、button20、button21 を配置します。
それぞれのボタンの Click イベントを準備しておきます。これらのイベントがタスクを実行して内部的に await するので、必ず async という予約語を追加してください。
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;
}
}
}