バリア (.NET Framework)

バリアとは、複数のスレッド (参加要素と呼ばれます) が段階的に 1 つのアルゴリズムで同時に動作できるようにするユーザー定義の同期プリミティブです。 各参加要素は、コードのバリア ポイントに到達するまで実行されます。 バリアは作業の 1 つのフェーズの終了を表します。 参加要素は、バリアに到達すると、すべての参加要素が同じバリアに到達するまでブロックされます。 すべての参加要素がバリアに到達した後、必要に応じて、フェーズ後の処理を呼び出すことができます。 フェーズ後の処理を使用すると、他のすべてのスレッドがブロックされた状態のまま 1 つのスレッドでアクションを実行できます。 アクションが実行された後、参加要素のブロックはすべて解除されます。

基本的なバリア パターンを次のコード スニペットに示します。


' Create the Barrier object, and supply a post-phase delegate 
' to be invoked at the end of each phase.
Dim barrier = New Barrier(2, Sub(bar)
                                 ' Examine results from all threads, determine 
                                 ' whether to continue, create inputs for next phase, etc. 
                                 If (someCondition) Then
                                     success = True
                                 End If
                             End Sub)



' Define the work that each thread will perform. (Threads do not
' have to all execute the same method.)
Sub CrunchNumbers(ByVal partitionNum As Integer)

    ' Up to System.Int64.MaxValue phases are supported. We assume
    ' in this code that the problem will be solved before that.
    While (success = False)

        ' Begin phase:
        ' Process data here on each thread, and optionally
        ' store results, for example:
        results(partitionNum) = ProcessData(myData(partitionNum))

        ' End phase:
        ' After all threads arrive,post-phase delegate
        ' is invoked, then threads are unblocked. Overloads
        ' accept a timeout value and/or CancellationToken.
        barrier.SignalAndWait()
    End While
End Sub

' Perform n tasks to run in in parallel. For simplicity
' all threads execute the same method in this example.
Shared Sub Main()

    Dim app = New BarrierDemo()
    Dim t1 = New Thread(Sub() app.CrunchNumbers(0))
    Dim t2 = New Thread(Sub() app.CrunchNumbers(1))
    t1.Start()
    t2.Start()
End Sub

 // Create the Barrier object, and supply a post-phase delegate 
 // to be invoked at the end of each phase.
 Barrier barrier = new Barrier(2, (bar) => 
     {
         // Examine results from all threads, determine 
         // whether to continue, create inputs for next phase, etc. 
         if (someCondition)
             success = true;
     });       


 // Define the work that each thread will perform. (Threads do not
 // have to all execute the same method.)
 void CrunchNumbers(int partitionNum)
 {
     // Up to System.Int64.MaxValue phases are supported. We assume
     // in this code that the problem will be solved before that.
     while (success == false)
     {
         // Begin phase:
         // Process data here on each thread, and optionally
         // store results, for example:
         results[partitionNum] = ProcessData(data[partitionNum]);

         // End phase:
         // After all threads arrive,post-phase delegate
         // is invoked, then threads are unblocked. Overloads
         // accept a timeout value and/or CancellationToken.
         barrier.SignalAndWait();
     }
 }

 // Perform n tasks to run in in parallel. For simplicity
// all threads execute the same method in this example.
 static void Main()
 {
     var app = new BarrierDemo();
     Thread t1 = new Thread(() => app.CrunchNumbers(0));
     Thread t2 = new Thread(() => app.CrunchNumbers(1));
     t1.Start();
     t2.Start();

 }

コード例全体については、「方法: バリアを使用して同時実行操作を同期する」を参照してください。

参加要素の追加と削除

Barrier を作成する場合は、参加要素の数を指定します。 また、参加要素は、いつでも動的に追加または削除できます。 たとえば、1 つの参加要素がその役割を果たした場合に、結果を保存し、そのスレッドの実行を停止してから、RemoveParticipant を呼び出してバリアの参加要素の数を 1 つ減らすことができます。 AddParticipant を呼び出して参加要素を追加すると、戻り値によって現在のフェーズ数が示されます。この値は、追加した新しい参加要素の作業を初期化する場合に役立つことがあります。

破損したバリア

いずれかの参加要素がバリアに到達できない場合、デッドロックが発生することがあります。 このようなデッドロックを避けるには、SignalAndWait メソッドのオーバーロードを使用して、タイムアウト時間とキャンセル トークンを指定します。 オーバーロードから返されるブール値は、次のフェーズに進む前にすべての参加要素でチェックできます。

フェーズ後の例外

フェーズ後のデリゲートから例外がスローされる場合、例外は BarrierPostPhaseException オブジェクトでラップされ、このオブジェクトがすべての参加要素に反映されます。

バリアと ContinueWhenAll

バリアは、スレッドで複数のフェーズをループ処理する場合に特に便利です。 作業のフェーズが 1 つか 2 つしかコードに必要ない場合は、次のような何らかの暗黙的な結合と共に System.Threading.Tasks.Task オブジェクトを使用することを検討してください。

詳細については、「方法: 複数のタスクを継続に連結する」を参照してください。

参照

処理手順

方法: バリアを使用して同時実行操作を同期する

その他の技術情報

スレッド処理オブジェクトと機能