Rendezvous w C#–klasa Barrier

Klasa Barrier umożliwia synchronizacje kilku wątków. Mechanizm bardzo znany np. z Ady pozwala dopuścić wykonanie kodu wyłącznie jeśli inne wątki na to się zgadzają. Załóżmy, że mamy 4 wątki robiące równoległe jakieś obliczenia. Po ich zakończeniu chcemy scalić wynik ale musimy poczekać aż wszystkie zadania zostaną ukończone. W C# 4.0 można wykorzystać tzw. taski, ale w tym artykule skupimy się na Barrier, która służy do dużo bardziej skomplikowanych zadań.

Przypuśćmy, że nasz algorytm składa się z kilku faz – z tym że faza następna nie może być  rozpoczęta dopóki wszystkie składowe fazy A nie zostały skończone. Zatem w fazie A, 4 wątki wykonują operacje, po ich zakończeniu rozpoczynamy fazę B itp. Nie możemy jednak rozpocząć fazy B dopóki faza A nie zostanie skończona przez wszystkie wątki.

Definiujemy więc barierę dla 4 uczestników:

     Barrier barrier = new Barrier(4, (b) =>
                                                 {
                                                     MessageBox.Show(string.Format("Wynik={0}, faza={1}", magicNumber,
                                                                                   b.CurrentPhaseNumber));
                                                 });

 

Pierwszy parametr to liczba uczestników (wątków) a drugi to callback wyświetlający numer właśnie zakończonej fazy oraz wyliczoną wartość w algorytmie. Każdy wątek zawiera taką samą logikę liczenia liczby:

  Action calculations = () =>
                                      {
                                          Interlocked.Increment(ref magicNumber);
                                          barrier.SignalAndWait(); // faza A zakończona                               

                                          Interlocked.Increment(ref magicNumber);
                                          barrier.SignalAndWait(); // faza B zakończona

                                          Interlocked.Increment(ref magicNumber);
                                          barrier.SignalAndWait(); // faza C zakończona

                                          Interlocked.Increment(ref magicNumber);
                                          barrier.SignalAndWait(); // faza D zakończona
                                      };

SignalAndWait wysyła sygnał do innych wątków oraz czeka (blokuje) aż inne wątki wyślą również sygnał. Jeśli zostanie uruchomionych więcej wątków niż liczba uczestników, Barrier wyrzuci wyjątek:

The number of threads using the barrier exceeded the total number of registered participants.

Następnie wystarczy uruchomić 4 wątki:

Parallel.Invoke(calculations, calculations, calculations, calculations);

Dobrym zwyczajem jest również wywołanie Dispose na Barrier:

barrier.Dispose();

Istnieje również możliwość zmieniania liczby uczestników już po inicjalizacji bariery:

barrier.AddParticipants(2);
barrier.RemoveParticipant();

Przykładowy kod kolejno powinien wyświetlić liczby 4,8,12,16. Mamy tego pewność ponieważ następna faza nie zostanie rozpoczęta dopóki poprzednia się nie zakończy a na koniec każdej fazy wynik zwiększa się o 4.

2 thoughts on “Rendezvous w C#–klasa Barrier”

  1. @wojtek:
    Barrier raczej w bardziej “skomplikowanych” aplikacjach wielowatkowych przyda sie. W prostych, Monitor wystarcza w zupelnosci. A w webowych to juz sprawa kompletnie sie upraszcza(przewaznie)…

Leave a Reply

Your email address will not be published.