Gewusst wie: Synchronisieren gleichzeitiger Vorgänge mit einer Barriere

Im folgenden Beispiel wird das Synchronisieren paralleler Aufgaben mit einer Barrier veranschaulicht.

Beispiel

Das folgende Programm zählt, wie viele Iterationen (oder Phasen) erforderlich sind, bis zwei Threads mit einem Zufallsalgorithmus zum erneuten Mischen der Wörter ihre Hälfte der Projektmappe in der gleichen Phase finden. Nachdem jeder Thread seine Wörter gemischt hat, werden die beiden Ergebnisse im Vorgang nach der Phase für die Barriere verglichen, um festzustellen, ob der vollständige Satz in der richtigen Wortfolge gerendert wurde.

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks


    Class Program
        Shared words1() = New String() {"brown", "jumped", "the", "fox", "quick"}
        Shared words2() = New String() {"dog", "lazy", "the", "over"}
        Shared solution = "the quick brown fox jumped 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
//#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",  "jumped", "the", "fox", "quick"};
        static string[] words2 = new string[] { "dog", "lazy","the","over"};
        static string solution = "the quick brown fox jumped 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();
            }
        }
    }
}

Eine Barrier ist ein Objekt, das verhindert, dass einzelne Aufgaben in einem Parallelvorgang fortgesetzt werden, bevor alle Aufgaben die Barriere erreichen. Dies ist nützlich, wenn ein Parallelvorgang in Phasen auftritt und jede Phase eine Synchronisierung der Aufgaben erfordert. In diesem Beispiel umfasst der Vorgang zwei Phasen. In der ersten Phase füllt jede Aufgabe ihren Abschnitt des Puffers mit Daten auf. Wenn die einzelnen Aufgabe das Auffüllen der Abschnitte beendet haben, signalisiert die Aufgabe der Barriere, dass sie fortgesetzt werden kann und wartet anschließend. Wenn alle Aufgaben der Barriere das entsprechende Signal gesendet haben, wird die Sperre für sie aufgehoben, und die zweiten Phase beginnt. Die Barriere ist erforderlich, da in der zweiten Phase jede Aufgabe Zugriff auf alle Daten benötigt, die bis zu diesem Punkt generiert wurden. Ohne die Barriere könnten die zuerst abgeschlossenen Aufgaben versuchen, in Puffern zu lesen, die noch nicht von anderen Aufgaben aufgefüllt wurden. Sie können auf diese Weise eine beliebige Anzahl von Phasen synchronisieren.

Siehe auch

Konzepte

Datenstrukturen für die parallele Programmierung