Compartir a través de


Barrera

Un System.Threading.Barrier es un primitivo de sincronización que permite que varios hilos (conocidos como participantes) trabajen simultáneamente en un algoritmo en distintas fases. Cada participante se ejecuta hasta que alcanza el punto de barrera en el código. La barrera representa el final de una fase de trabajo. Cuando un participante alcanza la barrera, se bloquea hasta que todos los participantes hayan alcanzado la misma barrera. Una vez que todos los participantes hayan alcanzado la barrera, puede invocar opcionalmente una acción posterior a la fase. Esta acción posterior a la fase se puede usar para realizar acciones mediante un único subproceso, mientras que todos los demás subprocesos siguen bloqueados. Una vez ejecutada la acción, todos los participantes se desbloquean.

El siguiente fragmento de código muestra un patrón de barrera 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 obtener un ejemplo completo, vea Cómo: sincronizar operaciones simultáneas con una barrera.

Agregar y quitar participantes

Al crear una Barrier instancia, especifique el número de participantes. También puede agregar o quitar participantes dinámicamente en cualquier momento. Por ejemplo, si un participante resuelve su parte del problema, puede almacenar el resultado, detener la ejecución en ese hilo y llamar Barrier.RemoveParticipant para disminuir el número de participantes de la barrera. Al agregar un participante llamando a Barrier.AddParticipant, el valor devuelto especifica el número de fase actual, que puede ser útil para inicializar el trabajo del nuevo participante.

Barreras rotas

Si un participante no consigue alcanzar la barrera, pueden producirse interbloqueos. Para evitar estos interbloqueos, use las sobrecargas del Barrier.SignalAndWait método para especificar un período de tiempo de espera y un token de cancelación. Estas sobrecargas devuelven un valor booleano que todos los participantes pueden comprobar antes de continuar con la siguiente fase.

Excepciones posteriores a la fase

Si el delegado posterior a la fase produce una excepción, se encapsula en un objeto BarrierPostPhaseException que después se propaga a todos los participantes.

Barrera frente a ContinueWhenAll

Las barreras resultan especialmente útiles cuando los subprocesos realizan varias fases en bucles. Si el código solo requiere una o dos fases de trabajo, considere si se deben usar System.Threading.Tasks.Task objetos con cualquier tipo de combinación implícita, entre las que se incluyen:

Para más información, consulte Encadenar tareas mediante tareas de continuación.

Consulte también