Udostępnij za pośrednictwem


Bariera

A System.Threading.Barrier jest elementem pierwotnym synchronizacji, który umożliwia wielu wątkom (znanym jako uczestnicy) równoczesne działanie algorytmu w fazach. Każdy uczestnik wykonuje, dopóki nie osiągnie punktu bariery w kodzie. Bariera reprezentuje koniec jednej fazy pracy. Gdy uczestnik osiągnie barierę, blokuje, dopóki wszyscy uczestnicy nie osiągną tej samej bariery. Po osiągnięciu bariery przez wszystkich uczestników możesz opcjonalnie wywołać akcję po fazie. Ta akcja po fazie może służyć do wykonywania akcji przez jeden wątek, podczas gdy wszystkie inne wątki są nadal blokowane. Po wykonaniu akcji wszyscy uczestnicy są odblokowani.

Poniższy fragment kodu przedstawia podstawowy wzorzec bariery.


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

Pełny przykład można znaleźć w temacie How to: sync concurrent operations with a Barrier (Instrukcje: synchronizowanie operacji współbieżnych za pomocą bariery).

Dodawanie i usuwanie uczestników

Podczas tworzenia Barrier wystąpienia określ liczbę uczestników. Możesz również dynamicznie dodawać lub usuwać uczestników. Jeśli na przykład jeden uczestnik rozwiąże swój problem, możesz zapisać wynik, zatrzymać wykonywanie w tym wątku i wywołać metodę Barrier.RemoveParticipant dekrementacji liczby uczestników w barierze. Po dodaniu uczestnika przez wywołanie Barrier.AddParticipantmetody zwracana wartość określa bieżący numer fazy, który może być przydatny w celu zainicjowania pracy nowego uczestnika.

Złamane bariery

Zakleszczenia mogą wystąpić, jeśli jeden z uczestników nie osiągnie bariery. Aby uniknąć tych zakleszczeń, użyj przeciążeń Barrier.SignalAndWait metody, aby określić limit czasu i token anulowania. Te przeciążenia zwracają wartość logiczną, którą każdy uczestnik może sprawdzić, zanim przejdzie do następnej fazy.

Wyjątki po fazie

Jeśli delegat po fazie zgłasza wyjątek, jest on opakowany w BarrierPostPhaseException obiekt, który jest następnie propagowany do wszystkich uczestników.

Bariera a kontynuacjaWszystkie

Bariery są szczególnie przydatne, gdy wątki wykonują wiele faz w pętlach. Jeśli kod wymaga tylko jednej lub dwóch faz pracy, rozważ, czy używać System.Threading.Tasks.Task obiektów z dowolnym rodzajem sprzężenia niejawnego, w tym:

Aby uzyskać więcej informacji, zobacz Łączenie zadań za pomocą zadań kontynuacji.

Zobacz też