БарьерBarrier

System.Threading.Barrier — это определяемый пользователем примитив синхронизации, позволяющий нескольким потокам (которые называются участниками) параллельно осуществлять поэтапную работу с алгоритмом.A System.Threading.Barrier is a synchronization primitive that enables multiple threads (known as participants) to work concurrently on an algorithm in phases. Каждый участник выполняется до достижения точки барьера в коде.Each participant executes until it reaches the barrier point in the code. Барьер означает окончание одного этапа работы.The barrier represents the end of one phase of work. Когда участник достигает барьера, он блокируется до тех пор, пока все участники не достигнут этого барьера.When a participant reaches the barrier, it blocks until all participants have reached the same barrier. Когда все участники достигли барьера, можно при необходимости можно вызвать действие следующего этапа.After all participants have reached the barrier, you can optionally invoke a post-phase action. Это действие следующего этапа может использоваться для выполнения действий одним потоком, пока все остальные потоки все еще остаются блокированными.This post-phase action can be used to perform actions by a single thread while all other threads are still blocked. После выполнения действия все участники разблокируются.After the action has been executed, the participants are all unblocked.

Во фрагменте кода ниже показан базовый шаблон барьера.The following code snippet shows a basic barrier pattern.


 // 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();
     
 }

' 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

Полный пример можно найти в руководстве по синхронизации параллельных операций с барьером.For a complete example, see How to: synchronize concurrent operations with a Barrier.

Добавление и удаление участниковAdding and removing participants

При создании экземпляра Barrier укажите количество участников.When you create a Barrier instance, specify the number of participants. Вы можете также динамически добавлять или удалять участников в любое время.You can also add or remove participants dynamically at any time. Например, если один участник решил свою часть задачи, можно сохранить результат, остановить выполнение этого потока и вызвать Barrier.RemoveParticipant, чтобы уменьшить число участников барьера.For example, if one participant solves its part of the problem, you can store the result, stop execution on that thread, and call Barrier.RemoveParticipant to decrement the number of participants in the barrier. При добавлении участника путем вызова Barrier.AddParticipant возвращаемое значение определяет номер текущего этапа, что может быть полезно для инициализации действий нового участника.When you add a participant by calling Barrier.AddParticipant, the return value specifies the current phase number, which may be useful in order to initialize the work of the new participant.

Неисправные барьерыBroken barriers

Если один из участников не достигает барьера, это может привести к возникновению взаимоблокировок.Deadlocks can occur if one participant fails to reach the barrier. Во избежание взаимных блокировок используйте перегрузки метода Barrier.SignalAndWait, чтобы задать время ожидания и токен отмены.To avoid these deadlocks, use the overloads of the Barrier.SignalAndWait method to specify a time-out period and a cancellation token. Эти перегрузки возвращают логическое значение, которое может проверить каждый участник перед переходом к следующему этапу.These overloads return a Boolean value that every participant can check before it continues to the next phase.

Исключения следующих этаповPost-phase exceptions

Если делегат следующего этапа создает исключение, оно инкапсулируется в объект BarrierPostPhaseException, который затем передается всем участникам.If the post-phase delegate throws an exception, it is wrapped in a BarrierPostPhaseException object which is then propagated to all participants.

Сравнение барьера и ContinueWhenAllBarrier versus ContinueWhenAll

Барьеры особенно полезны, когда потоки выполняют несколько этапов циклически.Barriers are especially useful when the threads are performing multiple phases in loops. Если код требует выполнения только в один–два этапа, рассмотрите возможность использования объектов System.Threading.Tasks.Task с любым видом неявного объединения, в том числе:If your code requires only one or two phases of work, consider whether to use System.Threading.Tasks.Task objects with any kind of implicit join, including:

Подробнее см. в разделе Создание цепочки задач с помощью задач продолжения.For more information, see Chaining Tasks by Using Continuation Tasks.

См. такжеSee also