Waiting for a specified number of threads using the Barrier Class

First off, if you are looking for the definitive online resource on threading in C#, don’t waste your time here. Go to Threading in C# by Joseph Albahari. This is without doubt the best resource anywhere on C# threading, in my opinion (for what it’s worth).

This said, I’m still going to create this post for my own reference

The Barrier Class

The Barrier class is a synchronization class which allows us to block until a set number of threads have signaled the Barrier. Each thread will signal and wait and thus be blocked by the barrier until the set number of signals has been reach at which time they will all be being unblocked, allowing them to continue. Unlike the CountdownEvent, when the Barrier has been signalled the specified number of times it resets and can block again and again awaiting for specified number of threads to signal.

Where would a synchronization class be without a real world analogy – so, let’s assume we are waiting for a known number of race horses (our threads) to arrive at a start barrier (our Barrier class) and once they all arrive we can release them. Only when they’ve all reached the end line (the Barrier class again) do we output the result of the race.

Let’s look at some code (note: this sample can just be dropped straight into a console app’s Program class)

static Barrier barrier = new Barrier(5, b => 
         Console.WriteLine("--- All horse have reached the barrier ---"));
static Random random = new Random();

static void Main()
{
   Task horse1 = Task.Run(() => Race("Horse1", random.Next(1, 1000)));
   Task horse2 = Task.Run(() => Race("Horse2", random.Next(1, 1000)));
   Task horse3 = Task.Run(() => Race("Horse3", random.Next(1, 1000)));
   Task horse4 = Task.Run(() => Race("Horse4", random.Next(1, 1000)));
   Task horse5 = Task.Run(() => Race("Horse5", random.Next(1, 1000)));

   // this is solely here to stop the app closing until all threads have completed
   Task.WaitAll(horse1, horse2, horse3, horse4, horse5);
}

static void Race(string horse, int speed)
{
   Console.WriteLine(horse + " is at the start gate");
   barrier.SignalAndWait();

   // wait a random amount of time before the horse reaches the finish line
   Task.Delay(speed);
   Console.WriteLine(horse + " reached finishing line");
   barrier.SignalAndWait();
}

Maybe I’ve taken the horse race analogy a little too far :)

Notice that the Barrier constructor allows us to add an action which is executed after each phase, i.e. when the specified number of threads have signalled the barrier.

We can add participants to the barrier at runtime, so if we were initially waiting for three threads and these created another three threads (for example), we could then notify the barrier that we want to add the three more threads using the AddParticipant or AddParticipants methods. Likewise we could reduce the participant count using RemoveParticipant or RemoveParticipants.

As you can see from the code above, when a thread wishes to signal it’s completed a phase of it’s processing it calls the SignalAndWait method on the barrier. With, as the name suggests, signals the Barrier class then waits until the Barrier releases it.