Geeks With Blogs

News

Series

Add to Technorati Favorites


An Archived Managed World This blog has moved to http://www.managed-world.com/blog

Well, I said I was going to be moving on to CountdownEvent, but I was wrong. there is one more aspect of using Barrier that I just had to share (thanks go to Stephen Toub on the Parallel Computing Platform team for bringing this up).

In my first Barrier post, I had mentioned:

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.

To make a long story short, this isn’t strictly true. Barrier can be used just fine when there is an unknown amount of work. The distinction between Barrier and other synchronization primitives isn’t about “known amount versus unknown amount of work”, it’s more about “usable once versus usable many times.” To explain this in more detail, let’s look back at the example from the last post (a Road Trip to Seattle).

Multiple Usages

First, it is perfectly allowed to use a Barrier more than once. Using the Road Trip metaphor, instead of going straight to Seattle after leaving the Gas Station, let’s have everybody stop for lunch on there way. So now there are two “sync points” that we have. First, we’ll wait for everybody to meet up at the gas station, then we’ll also wait for everybody to meet up for lunch after they’ve left the gas station.

Revising our DriveToSeattle method from the last post, we will simply call SignalAndWait() a second time to provide a second sync point where everybody meets for lunch:

static void DriveToSeattle(string name, TimeSpan timeDelay)
{
    // Perform some work
    Console.WriteLine("[{0}] Leaving House", name);
    Thread.Sleep(timeDelay);
    Console.WriteLine("[{0}] Arrived at Gas Station", name);

    // Need to wait for others
    sync.SignalAndWait();

    // Perform some work
    Console.WriteLine("[{0}] Leaving for Seattle", name);
    Thread.Sleep(timeDelay);
    Console.WriteLine("[{0}] Meet for lunch", name);

    // Need to wait for others
    sync.SignalAndWait();

    Console.WriteLine("[{0}] Leaving for Seattle", name);
}

In real-word usage of the Barrier primitive, this doesn’t have to be limited to two uses of the Barrier. We can use the Barrier any number of times we need to in order to provide these common synchronization points for our parallel code.

Unknown Number of Workers

I mentioned that it wasn’t strictly true that Barrier is great for a just a known amount of work. That’s because Barrier provides the functionality you need to even use it when the number of workers performing our work is unknown.

Back to our Road Trip. Dennis’s sister Dee is planning to come along on the Road Trip as well. However, she forgets to set her alarm and wakes up late. And unfortunately, Charlie, Mac, and Dennis have already left for Seattle. She still wants to go though so she plans to meet up with them for lunch.

What we need is for a way for Dee to flag “Hey guys! I’m coming!” And it just so happens that the Barrier class provides an AddParticipant method that we can use to make sure the extra “worker” (Dee in this case) is included the next time everybody waits for each other to “complete their work.”

In this case, Dee would simply “add herself” to the Barrier when her thread is spun up. To do this, let’s create a new DriveStraightToLunch method that will be used instead of the existing DriveToSeattle method we created before.

static void DriveStraightToLunch(string name, TimeSpan timeDelay)
{
    // "I'm coming along guys"
    sync.AddParticipant();

    // Meet for lunch
    Console.WriteLine("[{0}] Leaving for Seattle", name);
    Thread.Sleep(timeDelay);
    Console.WriteLine("[{0}] Meeting for lunch", name);

    // Need to wait for others
    sync.SignalAndWait();

    Console.WriteLine("[{0}] Leaving for Seattle", name);
}

And then we just spin up this new piece of work (added to our code in Main before all threads are joined):

... creation and start of Dennis thread

Thread.Sleep(TimeSpan.FromSeconds(6));
var dee = new Thread(() => DriveStraightToLunch("Dee", TimeSpan.FromSeconds(5)));
dee.Start();

dee.Join();
... join other threads

It’s that simple.

In Closing

I hope in the last two posts you’ve seen how useful the new Barrier synchronization primitive can be when writing Parallel Code. API-wise there really isn’t much to the new Barrier class. Of course, as is usual with writing parallel code, it’s learning to use it correctly or use it in the right situations that’s the tricky part :).

I would say "tune in next time when we chat about CountdownEvent” but considering this recent change of plans, I’m not going to tempt fate :). Later!

Posted on Tuesday, February 24, 2009 10:42 AM C# Dev Center , Concurrency , Development | Back to top


Comments on this post: An Intro to Barrier, cont.d

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Jason Olson | Powered by: GeeksWithBlogs.net