Barrier,中文被译为屏障。在C#中,可以用来实现多任务在多阶段中协同工作。通俗来讲,就是多个线程在执行到某个被共同指定步骤(即Barrier.SignalAndWait())的时候,就像遇到了屏障一样,必须等待其他还未执行到该步骤的线程。如果每个线程都执行到了该步骤,则大家又继续执行各自的逻辑。

Barrier对象可防止并行操作中的各个任务在所有任务到达屏障前继续执行。 如果并行操作分阶段执行,且每个阶段需要在任务之间进行同步,此对象就很有用。

在实例化Barrier对象的时候,还可以指定一个Action委托,该委托会在所有任务到达屏障后立即执行,接着各个任务又继续执行它们各自的逻辑。该Action委托只与Barrier对象关联,无论有多少个任务,只要它们都达到屏障后,该Action委托就会执行一次。

Barrier.SignalAndWait()该方法会通知Barrier对象,一个线程已经到达了屏障,需要等待其他还未到达屏障的线程。

 

1、普通使用方式

代码如下所示:

        //指定3个并行任务,当三个并行任务到达屏障后,就执行预先定义的Action委托
        //如果有第4个任务执行了SignalAndWait()方法,就会报错,因为当前Barrier对象只指定了3个可以并行的任务数量
        static Barrier barrier=new Barrier(3, it =>
        {
            Console.WriteLine($"Barrier委托 开始执行");
            Console.WriteLine($"Barrier委托 获取Barrier.CurrentPhaseNumber={barrier.CurrentPhaseNumber}");
            Thread.Sleep(2000);
            Console.WriteLine($"Barrier委托 执行完毕");
        });

        static void WorkOnBarrierOne(string threadName)
        {
            Console.WriteLine($"{threadName} 开始执行");
            Console.WriteLine($"{threadName} 获取Barrier.CurrentPhaseNumber={barrier.CurrentPhaseNumber}");
            Console.WriteLine($"{threadName} 到达屏障");
            barrier.SignalAndWait();
            Console.WriteLine($"{threadName} 再次获取Barrier.CurrentPhaseNumber={barrier.CurrentPhaseNumber}");
            Console.WriteLine($"{threadName} 执行完毕");
        }

        static void WorkOnBarrierTwo(string threadName) {
            Console.WriteLine($"{threadName} 开始执行");
            Console.WriteLine($"{threadName} 获取Barrier.CurrentPhaseNumber={barrier.CurrentPhaseNumber}");
            Thread.Sleep(1000);
            Console.WriteLine($"{threadName} 到达屏障");
            barrier.SignalAndWait();
            Console.WriteLine($"{threadName} 再次获取Barrier.CurrentPhaseNumber={barrier.CurrentPhaseNumber}");
            Console.WriteLine($"{threadName} 执行完毕");
        }

Main方法中的代码:

            Thread t1 = new Thread(() => WorkOnBarrierOne("线程1"));
            Thread t2 = new Thread(() => WorkOnBarrierTwo("线程2"));
            t1.Start();
            t2.Start();

            Console.WriteLine($"主线程 到达屏障");
            barrier.SignalAndWait();
            Console.WriteLine($"主线程 获取Barrier.CurrentPhaseNumber={barrier.CurrentPhaseNumber}");
            Console.WriteLine($"主线程 执行完毕");

结果:

 

 从结果中可以看出,只有指定的所有线程到达屏障(Barrier.SignalAndWait()方法处)之后,在Barrier对象中预先定义的Action委托就会执行,Action委托执行完毕之后,Barrier的CurrentPhaseNumber就会自增1,即表示当前阶段数量自增1。

Barrier可以执行很多阶段,可以将Barrier.SignalAndWait()方法放在一个循环代码中。

 

2、超时等待

可以在Barrier.SignalAndWait(int millisecondsTimeout)中使用超时等待结果。如果等待超时,则返回false,未超时,则返回true。

在超时等待之后,需要移除一个参与者,否则其他的线程就会一直阻塞等待。

因为超时等待后,就像一个人先跑了,而其他人到达屏障后并不知道有人先跑掉,他们会一直等待,因为只有人数在聚齐的情况下,他们才会又各自散去。

而此时,将先跑的人数减少,则剩下的就有机会聚齐。

Main代码如下所示:

 

            Thread t1 = new Thread(() => WorkOnBarrierOne("线程1"));
            Thread t2 = new Thread(() => WorkOnBarrierTwo("线程2"));
            t1.Start();
            t2.Start();

            Console.WriteLine($"主线程 到达屏障");
            //等待超时后,Barrier.SignalAndWait()会返回false,没有超时,会返回true
            if (!barrier.SignalAndWait(1000))
            {
                Console.WriteLine($"主线程 到达屏障后等待超时");
                //等待超时后,需要移除一个参与者,否则其他的线程就会一直等待
                //因为超时等待后,就像一个人先跑了,而其他人到达屏障后并不知道有人先跑掉,他们会一直等待,因为只有人数在聚齐的情况下,他们才会又各自散去
                //而此时,将先跑的人数减少,则剩下的就有机会聚齐
                barrier.RemoveParticipant();
            }

            Console.WriteLine($"主线程 获取Barrier.CurrentPhaseNumber={barrier.CurrentPhaseNumber}");
            Console.WriteLine($"主线程 执行完毕");

 

超时结果如下所示:

 

 

参考文献:https://docs.microsoft.com/zh-cn/dotnet/standard/threading/barrier

 posted on 2020-10-22 15:20  F风  阅读(854)  评论(0编辑  收藏  举报