在上篇文章中我们使用了Wait和Pulse 实现了Countdown
接下来我们可以使用刚刚写的Countdown 类来实现两个线程的交会。
{
static object _locker = new object ();
static Countdown _countdown = new Countdown( 2 );
public static void MainThread()
{
Random r = new Random();
new Thread(Mate).Start(r.Next( 1000 ));
Thread.Sleep(r.Next( 10000 )); // 主线程睡眠一段时间
_countdown.Singnal(); // 向_countdown注册信号,告知主线程已经来了。
_countdown.Wait(); // 等待其他线程
Console.WriteLine( " Mate! " );
}
static void Mate( object delay)
{
Thread.Sleep(( int )delay); // 线程睡眠。
_countdown.Singnal(); // 向_countdown注册信号,告知线程已经来了。
_countdown.Wait(); // 等待其他线程。
Console.WriteLine( " Mate! " );
}
}
就像小时候去春游一样,这里的_countdown就是老师,线程就是学生。
学生A早上睡觉,然后起床来到交会点,然后告诉老师,我来了,接着等待老师的出发命令,因为老师知道有两个学生要去春游,所以现在只来了一个,还有一个没有来,所以老师会让学生A等待,阻塞。
学生B也是睡觉,接着也来到交会点,告诉老师,我也来了,然后等待老师的出发命令。
当学生B告诉老师我来了的时候,此时老师的剩余等待学生计数为0,所以老师告诉这两个学生,你们可以出发了。
.net framework 4.0 提供了Barrier 的构造来实现线程交会的功能。如图所示:
Thread1 调用SignalAndWait告知Barrier,我已经来了,然后阻塞。
Thread3调用SignalAndWait告知Barrier,我已经来了,然后阻塞。
Thread2 调用SignalAndWait告知Barrier,我已经来了,Barrier知道现在三个线程都来了,所以让他们继续并发执行。
Barrier的方法简介:
- AddParticipants:增加参与者,也就是增加春游的人数。
-
RemoveParticipant :减少参与者,可能某人肚子痛,不能参加春游了。
-
SignalAndWait :参与者已经来了,并等待其他参与者的到来。
下面是使用Barrier的示例:
public 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
Barrier 的另一个非常有用的特性是在每一个阶段完成的时候你都可以执行一个post-phase 的action委托。
什么是阶段呢??,阶段就是参与者全部都到了的时候。
如果我们修改Barrier的构造函数如下:
static Barrier _barrier = new Barrier(3, (barrier) => Console.WriteLine());
//说明有三个参与者,并且每次三个参与者完成任务的时候执行Console.WriteLine方法.
那么我们的输出如下所示:
0 0 0
1 1 1
2 2 2
3 3 3
4 4 4
参考资料:
http://www.albahari.com/threading/
CLR Via C# 3.0