C# 浅析并行任务同步机制——Barrier类

Barrier:命名空间 System.Threading

              System.Threading.dll

               Barrier类的所有公共成员和受保护的成员都是线程安全的,并且可以从多个线程同时使用,但Dispose除外,必须仅在Barrier上的所有其他操作都已完成时才能使用

Barrier:非常适用于其中工作有多个任务分支且以后又需要合并的工作情况,使用也很广泛。

简单场景:假设有三个队伍拉练,队伍全部拉练完成之后,才算顺利完成

                 每个队伍拉练,需要经过四个步骤准备、开始、拉练和完成,队伍之间在每个阶段都是等待其他队伍完成之后,再进行下一步操作。

                 注:在声明Barrier对象时,第二个参数传递了一个委托,参数时传递的一个Barrier实例对象。可以从中获得一些需要的信息。

声明的任务类


 #region ==barrier例子==
    
    public abstract class HJ
    {
        public HJ(string name)
        {
            this.Name = name;
        }

        public string Name { get; set; }

        /// <summary>
        /// 第二阶段、开始
        /// </summary>
        public abstract void Begin();
        /// <summary>
        /// 第一阶段、准备
        /// </summary>
        public abstract void Prepare();
        /// <summary>
        /// 第三阶段、拉练
        /// </summary>
        public abstract void LaLian();
        /// <summary>
        /// 第四阶段、完成
        /// </summary>
        public abstract void Success();
        /// <summary>
        /// 总耗时
        /// </summary>
        protected int CoastTimes
        {
            get;set;
        }

        private static object objLock = new object();
        private static Random ran = null;

        protected static Random Ran
        {
            get
            {
                lock (objLock)
                {
                    if (ran == null)
                    {
                        ran = new Random();
                    }
                }
                return ran;
            }
        }

    }

    public class Hyj : HJ
    {
        
        public Hyj(string name) : base(name)
        {
        }

        public override void Begin()
        {
            WriteLine($"{this.Name} 第二阶段开始");
            int t = HJ.Ran.Next(7, 10);
            this.CoastTimes += t;
            WriteLine($"{this.Name} 还有 {t}小时 正在开始...");
            Thread.Sleep(t * 1000);
        }

        public override void LaLian()
        {
            WriteLine($"{this.Name} 第三阶段拉练");
            int t = HJ.Ran.Next(8, 13);
            this.CoastTimes += t;
            WriteLine($"{this.Name} 还有 {t}小时 正在拉练...");
            Thread.Sleep(t * 1000);
        }

        public override void Prepare()
        {
            WriteLine($"{this.Name} 第一阶段准备");
            int t = HJ.Ran.Next(3, 8);
            this.CoastTimes += t;
            WriteLine($"{this.Name} 需要还有 {t}小时 准备...");
            Thread.Sleep(t * 1000);
        }

        public override void Success()
        {
            WriteLine($"{this.Name} 第四阶段 耗时 {this.CoastTimes} 拉练完成");
        }
    }

    #endregion

第一种:正常情况下代码调用如下:

class Program
{
      static void Main(string[] args)
      {
           Begin();
           ReadLine();
      }
      
       private static void Begin()
       {
            Barrier barrier = new Barrier(3, it =>
             {
                 WriteLine();
                 WriteLine($"******* 阶段:{it.CurrentPhaseNumber}方法调用begin*******");
                 //每完成1此等待,执行一次这个方法,
                 WriteLine($"屏障中参与者的总数:{it.ParticipantCount}");
                 WriteLine($"******* 阶段:{it.CurrentPhaseNumber}方法调用end********");
                 WriteLine();
             });
            
            Parallel.For(0, 3, (e) =>
            {
                HJ hj = new Hyj($"队伍{e.ToString()}");
                hj.Prepare();
                barrier.SignalAndWait();
                hj.Begin();
                barrier.SignalAndWait();
                hj.LaLian();
                barrier.SignalAndWait();
                hj.Success();
                barrier.SignalAndWait();
                barrier.RemoveParticipant();
            });

            while (true)
            {
                if (barrier.ParticipantsRemaining == 0)
                {
                    WriteLine("资源已经使用完成,将要释放资源");
                    barrier.Dispose();
                    break;
                }
            }
      }
}

上面实现了队伍都完成拉练的情况下。但是实际过程中如果发生一些异常,但是当前任务不影响其他任务继续运行呢?

第二种情况:主动通知其他任务,当前任务运行出现问题,不在参与下面阶段了

       假设,我们并行任务中,有一个不是太重要的任务,运行过程中出现了问题,结束当前任务,但是其他任务需要继续完成,则我们可以用下面的实现方式。

       注:只将改变的代码贴出来,其他的不在粘贴上去。


Parallel.For(0, 3, (e) =>
{
     HJ hj = new Hyj($"队伍{e.ToString()}");
     hj.Prepare();
     barrier.SignalAndWait();
     hj.Begin();
     barrier.SignalAndWait();
     hj.LaLian();
     if (e.ToString() == "2")
     {
                    //此处表示了,当任务2运行过程中,出现问题,可以通过如下方式通知屏障,不需要等待当前任务了
                    
           WriteLine($"队伍{e.ToString()} 拉练过程中出现问题,退出,");
           barrier.RemoveParticipant();
           return;
      }
      barrier.SignalAndWait();
      hj.Success();
      barrier.SignalAndWait();
      barrier.RemoveParticipant();
});

当队伍2运行到异常代码处,调用Barrier类的RemoveParticipant()函数,通知Barrier,这个任务运行出现问题了,你们就不要等我了,该怎么进行就怎么进行吧。这样其他并行任务,在到达这个里时,就不在等待这个任务。

第三种情况,等待超时。假设,我们到达第三阶段时,我们的任务设置一个超时时间,如果超过了时间,不在等待延迟的任务。延迟的任务到达之后,自己执行完。如下:

注:只将改变的代码贴出来,其他的不在粘贴上去。


            
 Parallel.For(0, 3, (e) =>
 {
      HJ hj = new Hyj($"队伍{e.ToString()}");
      hj.Prepare();
      barrier.SignalAndWait();
      hj.Begin();
      barrier.SignalAndWait();
      hj.LaLian();
                //设置等待超时时间
      barrier.SignalAndWait(1000);
      hj.Success();
      barrier.SignalAndWait();
      barrier.RemoveParticipant();
});

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fervour

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值