Barrier クラス

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

−以上−