zoukankan      html  css  js  c++  java
  • Barrier实现并发同步

    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

  • 相关阅读:
    js处理json数据,java处理json数据
    sqlmap中##和$$的区别
    tar.gz和bin,以及rpm,deb等linux后缀的文件的区别
    ibatis内置类型
    1099端口被占问题
    动态代理与静态代理的区别
    条款36:绝不重新定义继承而来的non-virtual函数(Never redefine an inherited non-virtual function)
    条款35:考虑virtual函数以外的其他选择(Consider alternative to virtual functions)
    条款34:区分接口继承和实现继承(Different between inheritance of interface and inheritance of implemenation)
    工作好习惯(18之后)
  • 原文地址:https://www.cnblogs.com/williamwsj/p/13858474.html
Copyright © 2011-2022 走看看