Przeczytaj w języku angielskim

Udostępnij za pośrednictwem


Porady: synchronizacja jednoczesnych operacji za pomocą bariery

W poniższym przykładzie pokazano, jak synchronizować współbieżne zadania z elementem Barrier.

Przykład

Celem następującego programu jest zliczenie liczby iteracji (lub faz) wymaganych przez dwa wątki w celu znalezienia ich połowy rozwiązania w tej samej fazie przy użyciu algorytmu losowego do przetasowania słów. Po tym, jak każdy wątek przetasował swoje słowa, operacja bariery po fazie porównuje dwa wyniki, aby sprawdzić, czy pełne zdanie zostało renderowane w prawidłowej kolejności wyrazów.

C#
//#define TRACE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BarrierSimple
{
    class Program
    {
        static string[] words1 = new string[] { "brown",  "jumps", "the", "fox", "quick"};
        static string[] words2 = new string[] { "dog", "lazy","the","over"};
        static string solution = "the quick brown fox jumps over the lazy dog.";

        static bool success = false;
        static Barrier barrier = new Barrier(2, (b) =>
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < words1.Length; i++)
            {
                sb.Append(words1[i]);
                sb.Append(" ");
            }
            for (int i = 0; i < words2.Length; i++)
            {
                sb.Append(words2[i]);

                if(i < words2.Length - 1)
                    sb.Append(" ");
            }
            sb.Append(".");
#if TRACE
            System.Diagnostics.Trace.WriteLine(sb.ToString());
#endif
            Console.CursorLeft = 0;
            Console.Write("Current phase: {0}", barrier.CurrentPhaseNumber);
            if (String.CompareOrdinal(solution, sb.ToString()) == 0)
            {
                success = true;
                Console.WriteLine($"\r\nThe solution was found in {barrier.CurrentPhaseNumber} attempts");
            }
        });

        static void Main(string[] args)
        {

            Thread t1 = new Thread(() => Solve(words1));
            Thread t2 = new Thread(() => Solve(words2));
            t1.Start();
            t2.Start();

            // Keep the console window open.
            Console.ReadLine();
        }

        // Use Knuth-Fisher-Yates shuffle to randomly reorder each array.
        // For simplicity, we require that both wordArrays be solved in the same phase.
        // Success of right or left side only is not stored and does not count.
        static void Solve(string[] wordArray)
        {
            while(success == false)
            {
                Random random = new Random();
                for (int i = wordArray.Length - 1; i > 0; i--)
                {
                    int swapIndex = random.Next(i + 1);
                    string temp = wordArray[i];
                    wordArray[i] = wordArray[swapIndex];
                    wordArray[swapIndex] = temp;
                }

                // We need to stop here to examine results
                // of all thread activity. This is done in the post-phase
                // delegate that is defined in the Barrier constructor.
                barrier.SignalAndWait();
            }
        }
    }
}

Obiekt to Barrier obiekt, który uniemożliwia kontynuowanie poszczególnych zadań w ramach operacji równoległej do momentu osiągnięcia bariery przez wszystkie zadania. Jest to przydatne, gdy operacja równoległa występuje w fazach, a każda faza wymaga synchronizacji między zadaniami. W tym przykładzie istnieją dwie fazy operacji. W pierwszej fazie każde zadanie wypełnia sekcję buforu danymi. Gdy każde zadanie zakończy wypełnianie sekcji, zadanie sygnalizuje barierę, którą jest gotowa do kontynuowania, a następnie czeka. Gdy wszystkie zadania zasygnalizowały barierę, są one odblokowane, a druga faza rozpoczyna się. Bariera jest niezbędna, ponieważ druga faza wymaga, aby każde zadanie miało dostęp do wszystkich danych, które zostały wygenerowane do tego momentu. Bez bariery pierwsze zadania do wykonania mogą próbować odczytać z buforów, które nie zostały jeszcze wypełnione przez inne zadania. W ten sposób można zsynchronizować dowolną liczbę faz.

Zobacz też