An Archived Managed World

This blog has moved to http://www.managed-world.com/blog

  Home  |   Contact  |   Syndication    |   Login
  473 Posts | 0 Stories | 429 Comments | 1288 Trackbacks

News

Twitter












Tag Cloud


Archives

Post Categories

Blog Roll

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.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati
posted on Monday, February 09, 2009 11:17 PM

Feedback

# re: An Intro to Barrier 2/10/2009 8:29 AM Marquis Marc
That's a slick construct--I've had to roll my own Barrier on a couple of occasions, but I never had a good name for it like "Barrier".

I'm feeling strangely compelled to embark on a road trip to Seattle and try out this scenario.

# re: An Intro to Barrier 2/11/2009 5:47 AM Mark Smith
Nice example, Jason. Reading this, my summary would be - Barrier'ed thread code executes and waits until all threads taking part in the Barrier'ed operation reach a common sync point (the Barrier) in their executing code. Once they're all there, they resume execution in reverse order to their order of arrival at the sync point.

Does Barrier offer the ability to say /which/ threads are to be kicked off together, or for the threads to say which Barrier they want to Join?

# re: An Intro to Barrier 2/12/2009 11:48 AM freddy
I second Mark's question on barrier relation to the threads.

Basically, the issue I see with this is that I can see an exception (due to a bug or unexpected condition) leading to some threads being left waiting. I know u can restructure to avoid this, but then it isn't that clean ;)

# re: An Intro to Barrier 10/22/2009 5:43 PM Mohan
I cant figure out the difference between waitAll on threads vs. barrier. Why do we need to use barrier if waitAll already works the same way?

# re: An Intro to Barrier 1/26/2010 2:41 PM Fish Oil Supplements
Great info. This worked for me. I too had an innovative construct like Marquis did.

G

# re: An Intro to Barrier 8/12/2010 3:43 AM Internet Marketing Consultant
I'm having the same problem as Mohan. Can you please elaborate the difference between waitALL as he mentioned above?

# re: An Intro to Barrier 8/17/2010 12:52 PM sdf
awesome example!

# re: An Intro to Barrier 3/22/2011 7:04 PM Desmond Hardnette
yr this simple type of scenario is helpful for me!thx mate there.

# re: An Intro to Barrier 6/14/2011 8:44 AM Bill Geronatsios
Hi the same with TASK in .net 4 is :
-------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BarrierBest
{
class Program
{
private static Barrier sync;
static void Main(string[] args)
{

sync = new Barrier(3,(barrier)=>{
Console.WriteLine("Current Phase :{0}", barrier.CurrentPhaseNumber);
});
Task t1 = new Task(() =>
{
DriveToSeattle("Bill",TimeSpan.FromSeconds(1));
});
t1.Start();

Task t2 = new Task(() =>
{
DriveToSeattle("Michael", TimeSpan.FromSeconds(2));
});
t2.Start();
Task t3 = new Task(() =>
{
DriveToSeattle("Oliver", TimeSpan.FromSeconds(3));
});
t3.Start();
Task.WaitAll();
//wait for inpout before exit
Console.WriteLine("Press enter to finish");
Console.ReadLine();
}

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);
sync.SignalAndWait();
// Perform some more work
Console.WriteLine("[{0}] Leaving for Seattle", name);
}
}
}

-------------------------------------

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: