An Intro to Barrier

5 minutes read

Originally posted on: http://geekswithblogs.net/jolson/archive/2009/02/09/an-intro-to-barrier.aspx

In this first stage of our Tour de BCL, we will be passing through the new Barrier class.

So what is a Barrier? Let’s take a look at the boring technical description for a 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.

I don’t know about you, but sometimes technical descriptions like the above just sound like “blah, blah, blah” to me. What does a Barrier really mean to me, as a developer. Let’s break it down a different way by looking at a specific real-life scenario.

Think of a Road Trip

Instead of looking at the technical description above, think of the concept of a road trip.

There are three friends, Mac, Charlie, and Dennis. They live in a small town south of Seattle but want to take a road trip up to Seattle. So Mac calls up Charlie and Dennis and says “Hey guys, let’s take a road trip to Seattle. Meet up at the local gas station and then we’ll all leave from there.” So now they all now they need to go to the local gas station, wait for all of them to show up, and then they’ll leave for Seattle together and follow each other there.

Barrier

Relating this scenario to some code, let’s say that Charlie, Mac, and Dennis are all individual threads that have one method of execution, the DriveToSeattle() method. Within that method, they are all going “to drive” to the same gas station and then continue to drive to Seattle.

If we don’t use the Barrier to synchronize the work (aka wait for each other to arrive at the gas station), each one of them will leave for Seattle the second they arrive at the gas station:

static void Main(string[] args)
{
    var charlie = new Thread(() => {
        DriveToSeattle("Charlie", TimeSpan.FromSeconds(1))
    }); 
    charlie.Start();

    var mac = new Thread(() => {
        DriveToSeattle("Mac", TimeSpan.FromSeconds(2))
    }); 
    mac.Start();

    var dennis = new Thread(() => {
        DriveToSeattle("Dennis", TimeSpan.FromSeconds(3))
    }); 
    dennis.Start();

    charlie.Join();
    mac.Join();
    dennis.Join();

    Console.ReadKey();
}

static void DriveToSeattle(string name, TimeSpan timeToGasStation)
{
    // Drive to gas station
    Console.WriteLine("[{0}] Leaving House", name);
    Thread.Sleep(timeToGasStation);
    Console.WriteLine("[{0}] Arrived at Gas Station", name);

    // Need to sync here

    // Perform some more work
    Console.WriteLine("[{0}] Leaving for Seattle", name);
}

BeforeBarrier

Well, that’s hardly a road trip. If I were in Mac’s or Charlie’s shoes when Dennis left for Seattle without me, I know I would be a little upset. We obviously don’t want that to happen. So we can use the new Barrier to make sure they all “wait up” for each other at the gas station.

Using the new Barrier class

So how do you use the new Barrier class? It’s actually quite simple.The cool thing is that there are really only three things you need to learn to use the Barrier for this simple type of scenario: a single constructor and two method calls (both of which take zero parameters). Yes, it’s that simple to use.

Let’s look at what our new code using Barrier looks like (the three bold lines are the new ones):

static Barrier sync;

static void Main(string[] args)
{
    sync = new Barrier(participantCount:3);

    var charlie = new Thread(() => {
        DriveToSeattle("Charlie", TimeSpan.FromSeconds(1))
    }); 
    charlie.Start();

    var mac = new Thread(() => {
        DriveToSeattle("Mac", TimeSpan.FromSeconds(2))
    }); 
    mac.Start();

    var dennis = new Thread(() => {
        DriveToSeattle("Dennis", TimeSpan.FromSeconds(3))
    }); 
    dennis.Start();

    charlie.Join();
    mac.Join();
    dennis.Join();

    Console.ReadKey();
}

static void DriveToSeattle(string name, TimeSpan timeToGasStation)
{
    // Drive to gas station
    Console.WriteLine("[{0}] Leaving House", name);
    Thread.Sleep(timeToGasStation);
    Console.WriteLine("[{0}] Arrived at Gas Station", name);

    // Need to sync here
    sync.SignalAndWait();

    // Perform some more work
    Console.WriteLine("[{0}] Leaving for Seattle", name);
}

AfterBarrier

You can see now that nobody leaves for Seattle until everybody has arrived at the gas station. Essentially, with every call to SignalAndWait(), the number of signals received by the barrier is incremented. Once the number of signals received reaches the number of participants the Barrier was constructed with, all threads are then allowed to continue execution. And that’s all it takes to leverage the new Barrier class being introduced into the BCL with .NET Framework 4.0.

But Jason... What is up with that line of code: “sync = new Barrier(participantCount:3);”? I mean, what the heck is “participantCount:3”? How is that valid syntax?

Well, that’s easy. This is a new feature in C# 4.0 called Named Parameters where you can actually use a parameter’s name to specify what a value applies to. This becomes very handy in two places: 1) when combined with Optional Parameters, and 2) when used to clarify “magic numbers” (or “magic strings”, “magic booleans”, etc.) without having to introducing a temporary variable.

In this case, I could have left out participantCount and written just “sync = new Barrier(3);”. But I personally don’t like a magic number like “3” just floating around like this and Named Parameters give me an easy way to provide more context about the value “3”.

In Closing

So let’s look back at that boring technical description again:

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.

Think about it this way:

  • “stopping of execution between a number of threads...” = waiting for each other to arrive
  • “… at a given point” = the gas station
  • “prevents further execution until all threads… have reached the given point” = can’t leave for Seattle until everybody shows up

See? Nice and simple!

Barrier is a great new synchronization primitive to use when there is a known amount of work to do that is being done by different workers that all have common synchronization points. However, if the amount of work needing to be done is not well known beforehand, Barrier is not the greatest primitive to use. Next we’ll look at another new synchronization primitive coming in .NET Framework 4.0 that is great to use when the amount of work is not known: the CountdownEvent.

I hope you all enjoyed this first stage in our Tour de BCL. Until the next stage, we’ll see you later.

Updated:

Leave a Comment