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
−以上−