Barriere

Eine System.Threading.Barrier ist eine Synchronisierungsprimitive, die es mehreren (als Teilnehmer bezeichneten) Threads ermöglicht, in Phasen gleichzeitig an einem Algorithmus zu arbeiten. Jeder Teilnehmer wird ausgeführt, bis er den Barrierepunkt im Code erreicht hat. Die Barriere stellt das Ende einer Arbeitsphase dar. Wenn ein Teilnehmer die Barriere erreicht hat, wird er blockiert, bis alle Teilnehmer dieselbe Barriere erreicht haben. Nachdem alle Teilnehmer die Barriere erreicht haben, können Sie eine Nachphasenaktion aufrufen. Diese Nachphasenaktion kann verwendet werden, um Aktionen über einen einzelnen Thread auszuführen, während alle anderen Threads weiterhin blockiert sind. Nachdem die Aktion ausgeführt wurde, wird die Blockierung aller Teilnehmer aufgehoben.

Der folgende Codeausschnitt zeigt ein grundlegendes Barrieremuster.


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

Ein vollständiges Beispiel finden Sie unter Gewusst wie: Synchronisieren gleichzeitiger Vorgänge mit einer Barriere.

Hinzufügen und Entfernen von Teilnehmern

Wenn Sie eine Barrier-Instanz erstellen, geben Sie die Anzahl von Teilnehmern an. Sie können auch jederzeit Teilnehmer dynamisch hinzufügen oder entfernen. Wenn ein Teilnehmer z.B. seinen Teil des Problems gelöst hat, können Sie das Ergebnis speichern, die Ausführung auf diesem Thread beenden und Barrier.RemoveParticipant aufrufen, um die Anzahl von Teilnehmern in der Barriere zu verringern. Wenn Sie einen Teilnehmer hinzufügen, indem Sie Barrier.AddParticipant aufrufen, gibt der Rückgabewert die aktuelle Phasennummer an, die möglicherweise nützlich ist, um die Arbeit des neuen Teilnehmers zu initialisieren.

Fehlerhafte Barrieren

Deadlocks können auftreten, wenn ein Teilnehmer die Barriere nicht erreicht. Um diese Deadlocks zu vermeiden, verwenden Sie die Überladungen der Barrier.SignalAndWait-Methode, um ein Timeout und ein Abbruchtoken anzugeben. Diese Überladungen geben einen booleschen Wert zurück, den jeder Teilnehmer überprüfen kann, bevor er zur nächsten Phase springt.

Nachphasenausnahmen

Wenn der Nachphasendelegat eine Ausnahme auslöst, wird diese in ein BarrierPostPhaseException-Objekt eingeschlossen, das dann an alle Teilnehmer verteilt wird.

Barriere und ContinueWhenAll

Barrieren sind besonders nützlich, wenn die Threads mehrere Phasen in Schleifen ausführen. Wenn Ihr Code nur eine oder zwei Arbeitsphasen erfordert, sollten Sie in Betracht ziehen, System.Threading.Tasks.Task-Objekte mit einer beliebigen Art von implizitem Join zu verwenden, einschließlich:

Weitere Informationen finden Sie unter Verketten von Aufgaben mithilfe von Fortsetzungsaufgaben.

Siehe auch