Last Updated 2011/09/21
バリアについては、用語の解説の「バリア」でも触れましたが、フェーズの終了を待機するポイントです。ここに「フェーズ」という言葉が出てきました。用語の解説の「フェーズ」の項で、詳しくは Barrier クラスのページでと書きましたので、まず「フェーズ」について説明します。
次のコードの実行結果を見てください。task1、task2、task3 はバリアに参加したタスクです。「フェーズ後のアクション」は 3 つのタスクがバリアに達したあとに呼び出されるデリゲートですが、この行の CurrentPhaseNumber がこのときのフェーズの番号です。以下のコード例では「フェーズ後のアクション」の行は 3 行ありますが、バリアに達するのは 1 回きりではなく、いくつかの場面(これをフェーズと呼ぶのですが)で発生しています。フェーズがバリアに達するのがどういうタイミングなのかについては、.NET Framework SDK の中にも解説がありません。しかし、実行結果を見る限りでは、3 つのタスクが同じ状態になったときと考えられます。決して、3 つのタスクが終了したときではありません。
さて、実行結果の最後の行を見てください。「WaitAll 後」の行で task1.Status=RanToCimpletion とありますが、task2 および task3 も同じステータスになります。つまり、バリアに参加したすべてのタスクが終了し、バリアに達したかどうかはそれらのステータスを調べて確認しなければならないということになります。
private void button1_Click(object sender, RoutedEventArgs e)
{
// Barrier オブジェクトを作成する
// バリアに達したときに実行するメソッドは BarrierAction とする
Barrier barrier = new Barrier(3, BarrierAction);
// Action<object> オブジェクトを作成する
// 実行するデリゲートは UseBarrier メソッドとする
Action<object> action = UseBarrier;
// 3 つのタスクを設定する
Task task1 = Task.Factory.StartNew(action, barrier);
Debug.WriteLine("task1 をスタート");
Task task2 = Task.Factory.StartNew(action, barrier);
Debug.WriteLine("task2 をスタート");
Task task3 = Task.Factory.StartNew(action, barrier);
Debug.WriteLine("task3 をスタート");
// 実行したタスクをここで待機する
Task.WaitAll(t1, t2, t3);
Debug.WriteLine(String.Format("WaitAll 後: task1.Status={0} CurrentPhaseNumber={1}
ParticipantsRemaining={2} ParticipantCount={3}\r\n",
task1.Status, barrier.CurrentPhaseNumber, barrier.ParticipantsRemaining, barrier.ParticipantCount));
barrier.Dispose(); // 必須
}
// いわゆる、フェーズ後のアクションである
private void BarrierAction(Barrier barrier)
{
Debug.WriteLine(String.Format("フェーズ後アクション CurrentPhaseNumber={0} ParticipantsRemaining={1}
ParticipantCount={2}",
barrier.CurrentPhaseNumber, barrier.ParticipantsRemaining, barrier.ParticipantCount));
}
// タスクの実行デリゲート
private void UseBarrier(object obj)
{
Barrier barrier = obj as Barrier;
if (barrier != null)
{
for (int i = 0; i < 3; ++i)
{
Thread thread = Thread.CurrentThread;
int threadId = thread.ManagedThreadId;
Debug.WriteLine(String.Format("待機前のスレッド ID: {0} インデックス: {1} ThreadState: {2}",
threadId, i, thread.ThreadState));
barrier.SignalAndWait();
Debug.WriteLine(String.Format("CurrentPhaseNumber : {0}", barrier.CurrentPhaseNumber));
Debug.WriteLine(String.Format("待機後のスレッド ID: {0} インデックス: {1} ThreadState: {2}",
threadId, i, thread.ThreadState));
}
}
}
実行結果:
task1 をスタート task2 をスタート task3 をスタート 待機前のスレッド ID: 6 インデックス: 0 ThreadState: Background 待機前のスレッド ID: 12 インデックス: 0 ThreadState: Background 待機前のスレッド ID: 11 インデックス: 0 ThreadState: Background フェーズ後アクション: CurrentPhaseNumber=0 ParticipantsRemaining=3 ParticipantCount=3 待機後のスレッド ID: 11 インデックス: 0 ThreadState: Background 待機後のスレッド ID: 6 インデックス: 0 ThreadState: Background 待機後のスレッド ID: 12 インデックス: 0 ThreadState: Background 待機前のスレッド ID: 11 インデックス: 1 ThreadState: Background 待機前のスレッド ID: 12 インデックス: 1 ThreadState: Background 待機前のスレッド ID: 6 インデックス: 1 ThreadState: Background フェーズ後アクション: CurrentPhaseNumber=1 ParticipantsRemaining=3 ParticipantCount=3 待機後のスレッド ID: 6 インデックス: 1 ThreadState: Background 待機前のスレッド ID: 6 インデックス: 2 ThreadState: Background 待機後のスレッド ID: 11 インデックス: 1 ThreadState: Background 待機後のスレッド ID: 12 インデックス: 1 ThreadState: Background 待機前のスレッド ID: 11 インデックス: 2 ThreadState: Background 待機前のスレッド ID: 12 インデックス: 2 ThreadState: Background フェーズ後アクション: CurrentPhaseNumber=2 ParticipantsRemaining=3 ParticipantCount=3 待機後のスレッド ID: 12 インデックス: 2 ThreadState: Background 待機後のスレッド ID: 11 インデックス: 2 ThreadState: Background 待機後のスレッド ID: 6 インデックス: 2 ThreadState: Background WaitAll 後: task1.Status=RanToCompletion CurrentPhaseNumber=3 ParticipantsRemaining=3 ParticipantCount=3
−以上−