Barrier(屏障)是一种自定义的同步原语(synchronization primitive),它解决了多个线程(参与者)在多个阶段之间的并发和协调问题。
1)多个参与者执行相同的几个阶段的操作
2)在每一个阶段内,多个参与者并发执行
3)一个屏障点代表一个阶段的结束
4)一个参与者运行至屏障点时被阻塞,需要等待其他参与者都到达屏障点
5)所有参与者都到达了屏障点,才可以进入下一个阶段
创建Barrier时,需要指定参与者数量。它允许动态的添加或者删除参与者。
Barrier适合多个线程执行多个阶段的操作。如果只有一个或两个阶段,可以考虑ContinueWhenAll。
下面是一个简单的例子。4个线程执行执行4个阶段。
四个阶段的代码。(每个阶段只是输出文本,表示某个阶段的某个任务结束)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private static void RunPhase1( int taskId) { Console.WriteLine( "phase 1, task {0} completed" , taskId); } private static void RunPhase2( int taskId) { Console.WriteLine( "phase 2, task {0} completed" , taskId); } private static void RunPhase3( int taskId) { Console.WriteLine( "phase 3, task {0} completed" , taskId); } private static void RunPhase4( int taskId) { Console.WriteLine( "phase 4, task {0} completed" , taskId); } |
Barrier代码。(定义参与者数量,以及到达屏障点所做的事情,即输出文本宝石当前阶段结束)
1
2
3
4
5
|
private static Barrier barrier = new Barrier(4, (b) => { Console.WriteLine( "phase {0} completed" , b.CurrentPhaseNumber); Console.WriteLine(); }); |
Main方法代码。(创建4个任务,执行四个阶段,每个阶段结束时调用SignalAndWait方法设立屏障点)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
static void Main( string [] args) { var tasks = new List<Task>(); for ( var i = 0; i < 4; i++) { tasks.Add(Task.Factory.StartNew((index) => { var taskId = ( int )index; RunPhase1(taskId); barrier.SignalAndWait(); RunPhase2(taskId); barrier.SignalAndWait(); RunPhase3(taskId); barrier.SignalAndWait(); RunPhase4(taskId); barrier.SignalAndWait(); }, i)); } var finalTask = Task.Factory.ContinueWhenAll(tasks.ToArray(), (taskList) => { Task.WaitAll(taskList); Console.WriteLine( "all phases completed" ); barrier.Dispose(); }); finalTask.Wait(); Console.ReadLine(); } |
运行代码,查看结果。您会发现4个任务在每个阶段的完成顺序不太一样,所有任务完成后才进入下一个阶段。
再来看一个示例:
static void Main(string[] args) { var t1 = new Thread(() => PlayMusic("the 吉他手", "演奏一首精彩的独奏曲", 5)); var t2 = new Thread(() => PlayMusic("the 歌手", "唱他的歌", 2)); t1.Start(); t2.Start(); Console.Read(); } static Barrier _barrier = new Barrier(2, b => Console.WriteLine("完成阶段 {0}", b.CurrentPhaseNumber + 1)); static void PlayMusic(string name, string message, int seconds) { for (int i = 1; i < 3; i++) { //每个线程,先完成一些任务 Console.WriteLine("----------------------------------------------"); Thread.Sleep(TimeSpan.FromSeconds(seconds)); Console.WriteLine("{0} 开始 {1}", name, message); Thread.Sleep(TimeSpan.FromSeconds(seconds)); Console.WriteLine("{0} 完成 {1}", name, message); //完成了一个阶段任务,每个线程到这里都会执行一个回调函数,打印出阶段内容(_barrier对象中的匿名函数) _barrier.SignalAndWait(); } }