Partilhar via


Barreira

A System.Threading.Barrier é uma primitiva de sincronização que permite que vários threads (conhecidos como participantes) trabalhem simultaneamente em um algoritmo em fases. Cada participante executa até atingir o ponto de barreira no código. A barreira representa o fim de uma fase do trabalho. Quando um participante atinge a barreira, ela bloqueia até que todos os participantes tenham atingido a mesma barreira. Depois de todos os participantes terem atingido a barreira, você pode, opcionalmente, invocar uma ação pós-fase. Essa ação pós-fase pode ser usada para executar ações por um único thread enquanto todos os outros threads ainda estão bloqueados. Após a execução da ação, os participantes são todos desbloqueados.

O trecho de código a seguir mostra um padrão de barreira básico.


// 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 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 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

Para obter um exemplo completo, consulte Como sincronizar operações simultâneas com uma barreira.

Adicionar e remover participantes

Ao criar uma Barrier instância, especifique o número de participantes. Você também pode adicionar ou remover participantes dinamicamente a qualquer momento. Por exemplo, se um participante resolver sua parte do problema, você poderá armazenar o resultado, interromper a execução nesse thread e chamar Barrier.RemoveParticipant para diminuir o número de participantes na barreira. Quando você adiciona um participante chamando Barrier.AddParticipant, o valor de retorno especifica o número de fase atual, que pode ser útil para inicializar o trabalho do novo participante.

Barreiras quebradas

Podem ocorrer impasses se um participante não conseguir alcançar a barreira. Para evitar esses impasses, use as Barrier.SignalAndWait sobrecargas do método para especificar um período de tempo limite e um token de cancelamento. Essas sobrecargas retornam um valor booleano que cada participante pode verificar antes de continuar para a próxima fase.

Exceções pós-fase

Se o delegado pós-fase lançar uma exceção, ele será encapsulado em um BarrierPostPhaseException objeto que será propagado para todos os participantes.

Barreira versus ContinuarQuandoTodos

As barreiras são especialmente úteis quando os threads estão executando várias fases em loops. Se o seu código requer apenas uma ou duas fases de trabalho, considere se deve usar System.Threading.Tasks.Task objetos com qualquer tipo de junção implícita, incluindo:

Para obter mais informações, consulte Encadeando tarefas usando tarefas de continuação.

Consulte também