Gewusst wie: Synchronisieren gleichzeitiger Vorgänge mit einer Barriere

Das folgende Beispiel zeigt das Synchronisieren gleichzeitiger Aufgaben mit einer Barrier.

Beispiel

Zweck des folgenden Programms ist, zu zählen, wie viele Iterationen (oder Phasen) erforderlich sind, damit zwei Threads ihre jeweilige Hälfte der Lösung mithilfe eines Zufallsalgorithmus zum erneuten Mischen der Wörter in der gleichen Phase finden. Nachdem jeder Thread seine Wörter gemischt hat, vergleicht der Barrierennachphasen-Vorgang die beiden Ergebnisse, um festzustellen, ob der vollständige Satz in der richtigen Reihenfolge gerendert wurde.

//#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 {0} attempts", barrier.CurrentPhaseNumber);
            }
        });

        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();
            }
        }
    }
}
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks


Class Program
    Shared words1() = New String() {"brown", "jumps", "the", "fox", "quick"}
    Shared words2() = New String() {"dog", "lazy", "the", "over"}
    Shared solution = "the quick brown fox jumps over the lazy dog."

    Shared success = False
    Shared barrier = New Barrier(2, Sub(b)
                                        Dim sb = New StringBuilder()
                                        For i As Integer = 0 To words1.Length - 1
                                            sb.Append(words1(i))
                                            sb.Append(" ")
                                        Next
                                        For i As Integer = 0 To words2.Length - 1

                                            sb.Append(words2(i))

                                            If (i < words2.Length - 1) Then
                                                sb.Append(" ")
                                            End If
                                        Next
                                        sb.Append(".")
                                        System.Diagnostics.Trace.WriteLine(sb.ToString())

                                        Console.CursorLeft = 0
                                        Console.Write("Current phase: {0}", barrier.CurrentPhaseNumber)
                                        If (String.CompareOrdinal(solution, sb.ToString()) = 0) Then
                                            success = True
                                            Console.WriteLine()
                                            Console.WriteLine("The solution was found in {0} attempts", barrier.CurrentPhaseNumber)
                                        End If
                                    End Sub)

    Shared Sub Main()
        Dim t1 = New Thread(Sub() Solve(words1))
        Dim t2 = New Thread(Sub() Solve(words2))
        t1.Start()
        t2.Start()

        ' Keep the console window open.
        Console.ReadLine()
    End Sub

    ' 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.       
    Shared Sub Solve(ByVal wordArray As String())
        While success = False
            Dim rand = New Random()
            For i As Integer = 0 To wordArray.Length - 1
                Dim swapIndex As Integer = rand.Next(i + 1)
                Dim temp As String = wordArray(i)
                wordArray(i) = wordArray(swapIndex)
                wordArray(swapIndex) = temp
            Next

            ' 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()
        End While
    End Sub
End Class

Eine Barrier ist ein Objekt, das verhindert, dass einzelne Aufgaben in einem parallelen Vorgang fortgesetzt werden, bevor alle Aufgaben die Barriere erreichen. Das ist nützlich, wenn ein parallel ausgeführter Vorgang in Phasen erfolgt, und jede Phase eine Synchronisierung zwischen Aufgaben erfordert. In diesem Beispiel besteht der Vorgang aus zwei Phasen. In der ersten Phase füllt jede Aufgabe ihren Abschnitt des Puffers mit Daten. Wenn eine Aufgabe ihren Abschnitt gefüllt hat, signalisiert die Aufgabe der Barriere, das sie zum Fortfahren bereit ist, und wartet. Wenn alle Aufgaben ihr Signal an die Barriere gesendet haben, werden sie entsperrt, und die zweite Phase startet. Die Barriere ist erforderlich, da die zweite Phase voraussetzt, dass jede Aufgabe Zugriff auf alle Daten hat, die bis zu diesem Punkt generiert wurden. Ohne die Barriere könnten die Aufgaben, die zuerst ihre Abschnitte gefüllt haben, versuchen, aus Puffern zu lesen, die noch nicht von anderen Aufgaben gefüllt wurden. Sie können eine beliebige Anzahl von Phasen auf diese Weise synchronisieren.

Siehe auch