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();
});