The Barrier
class is a signaling construct new to Framework 4.0. It implements a thread execution barrier, which allows many threads to rendezvous at a point in time. The class is very fast and efficient, and is built upon Wait, Pulse
, and spinlocks.
To use this class:
- Instantiate it, specifying how many threads should partake in the rendezvous (you can change this later by calling
AddParticipants
/RemoveParticipants
). - Have each thread call
SignalAndWait
when it wants to rendezvous.
Instantiating Barrier
with a value of 3
causes SignalAndWait
to block until that method has been called three times. But unlike a CountdownEvent
, it then automatically starts over: calling SignalAndWait
again blocks until called another three times. This allows you to keep several threads “in step” with each other as they process a series of tasks.
In the following example, each of three threads writes the numbers 0 through 4, while keeping in step with the other threads:
static Barrier _barrier = new Barrier (3);
static void Main() {
new Thread (Speak).Start();
new Thread (Speak).Start();
new Thread (Speak).Start(); }
static void Speak() {
for (int i = 0; i < 5; i++)
{ Console.Write (i + " ");
_barrier.SignalAndWait(); } }
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
A really useful feature of Barrier
is that you can also specify a post-phase action when constructing it. This is a delegate that runs after SignalAndWait
has been called n times, but before the threads are unblocked. In our example, if we instantiate our barrier as follows:
static Barrier _barrier = new Barrier (3, barrier => Console.WriteLine());
then the output is:
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
A post-phase action can be useful for coalescing data from each of the worker threads. It doesn’t have to worry about preemption, because all workers are blocked while it does its thing.
Another way of Explaination
在Visual Studio 2010 and .NET Framework 4 Training Kit中有个System.Threading.Barrier的Demo,通过Barrier Class我们可以控制线程的运行,做到线程同步的效果。
Barrier Class在使用上十分的简单,只要在Barrier的构造函数中传入participantCount(简单的说就是要等待的线程个数),并在要同步的点调用SignalAndWait方法就可以了。线程会在调用SignalAndWait之后暂停运行,等待所有参与的线程都到达了同步点才继续往下运行。
举个例子来看,假设今天Charlie、Mac、Dennis三个人相约要去西雅图喝咖啡。由于三个人的住的地区不尽相同,且车子都需要加油,因此他们约在途中会经过的加油站待会合后一同前往。这样的情境我们可以通过Thread与Barrier用程序仿真出来。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace BarrierDemo
{
class Program
{
static Barrier sync;
static CancellationToken token;
static void Main(string[] args)
{
var source = new CancellationTokenSource();
token = source.Token;
sync = new Barrier(3);
var charlie = new Thread(() => DriveToBoston("Charlie", TimeSpan.FromSeconds(1))); charlie.Start();
var mac = new Thread(() => DriveToBoston("Mac", TimeSpan.FromSeconds(2))); mac.Start();
var dennis = new Thread(() => DriveToBoston("Dennis", TimeSpan.FromSeconds(3))); dennis.Start();
//source.Cancel();
charlie.Join();
mac.Join();
dennis.Join();
Console.ReadKey();
}
static void DriveToBoston(string name, TimeSpan timeToGasStation)
{
try
{
Console.WriteLine("[{0}] Leaving House", name);
// Perform some work
Thread.Sleep(timeToGasStation);
Console.WriteLine("[{0}] Arrived at Gas Station", name);
// Need to sync here
sync.SignalAndWait(token);
// Perform some more work
Console.WriteLine("[{0}] Leaving for Boston", name);
}
catch (OperationCanceledException)
{
Console.WriteLine("[{0}] Caravan was cancelled! Going home!", name);
}
}
}
}