Friday, 31 March 2017

Threading 07 Barrier

A Barrier is a synchronization primitive that enforces the stopping of execution between a number of threads or processes at a given point and prevents further execution until all threads or processors have reached the given point.

you can think of a barrier as a gate that all of your threads have to reach before moving forward. for example take a look at the following multi thread application.

using System;
using System.Threading;

namespace pc.threadBarrier
{
    class Program
    {
        static Random rnd = new Random(DateTime.Now.Millisecond);
        static void Main(string[] args)
        {
            var pawel = new Thread(CompleteTask);
            pawel.Name = "Pawel";

            var magda = new Thread(CompleteTask);
            magda.Name = "Magda";

            var tomek = new Thread(CompleteTask);
            tomek.Name = "Tomek";

            var jakub = new Thread(CompleteTask);
            jakub.Name = "jakub";

            var marin = new Thread(CompleteTask);
            marin.Name = "Marin";

            var threads = new Thread[] { pawel, magda, tomek, jakub, marin };

            for (int i = 0; i < threads.Length; i++)
                threads[i].Start(((char)(65+i)).ToString());
        }

        static void CompleteTask(object o)
        {
            var sleep = rnd.Next(1000, 2000);
            var s1 = $"{Thread.CurrentThread.Name} is sleeping for {sleep * .001}'s";
            Console.WriteLine(s1);
            Thread.Sleep(sleep);

            Thread.Sleep((int)Char.Parse(o.ToString()));
            var s2 = $"{Thread.CurrentThread.Name} has completed {o.ToString()}";
            Console.WriteLine(s2);
        }
    }
}


we start by creating five threads and passing them a task A to E, each letter has a higher ascii value 65 to 69, this is why after our threads sleep for a random number each one sleeps to the equivalent value of their task letter to ensure that they're printed in the ascending order. however because first each task sleeps for a random time between 1 and 2 seconds this is not possible, what we need is to sync all of our threads after their first sleep.

To sync our threads we use the barrier class.

using System;
using System.Threading;

namespace pc.threadBarrier
{
    class Program
    {
        static Random rnd = new Random(DateTime.Now.Millisecond);
        static Barrier barrier = new Barrier(participantCount:5);
        static void Main(string[] args)
        {
            var pawel = new Thread(CompleteTask);
            pawel.Name = "Pawel";

            var magda = new Thread(CompleteTask);
            magda.Name = "Magda";

            var tomek = new Thread(CompleteTask);
            tomek.Name = "Tomek";

            var jakub = new Thread(CompleteTask);
            jakub.Name = "jakub";

            var marin = new Thread(CompleteTask);
            marin.Name = "Marin";

            var threads = new Thread[] { pawel, magda, tomek, jakub, marin };

            for (int i = 0; i < threads.Length; i++)
                threads[i].Start(((char)(65+i)).ToString());
        }

        static void CompleteTask(object o)
        {
            var sleep = rnd.Next(1000, 2000);
            var s1 = $"{Thread.CurrentThread.Name} is sleeping for {sleep * .001}'s";
            Console.WriteLine(s1);
            Thread.Sleep(sleep);

            //pause thread until participant count hits 5
            barrier.SignalAndWait();
            Thread.Sleep((int)Char.Parse(o.ToString()) * 10);
            var s2 = $"{Thread.CurrentThread.Name} has completed {o.ToString()}";
            Console.WriteLine(s2);
        }
    }
}


after declaring a barrier that all of our threads share we signalandwait, wich will pause all the treads at that point until there are an equal number of signals by the threads as there are participants defined in the constructor of the barrier.