在做多线程操作时,难免会遇到一个线程需要等待另外一个线程操作完成后再继续执行的情况

这就涉及到线程与线程之前的通讯问题,难搞喔。。

信号量

前段时间在逛论坛的时候无意间发现了一个名词:信号量(Semaphore)

#百度百科

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。

C#信号量

在C#中,封装了有ManualResetEvent,AutoResetEvent等帮助类帮助我们很好的处理上面提到的问题。

线程可以通过调用帮助类对象的WaitOne()方法进入等待状态,然后另外一个线程通过调用AutoResetEvent对象的Set()方法取消等待的状态。

AutoResetEvent

表示一个线程同步事件,该事件在发出信号时会在释放单个等待线程后自动重置。这个类不能被继承。

使用AutoResetEvent实例等待完成后,会重置阻塞状态,也就是Set后只能等待通过一次

ManualResetEvent

AutoResetEvent.WaitOne()会自动改变事件对象的状态,即AutoResetEvent.WaitOne()每执行一次,事件的状态就改变一次,也就是从无信号变为有信号,或从有信号变为无信号。而ManualResetEvent则是调用Set()方法后其信号量不会自动改变,除非再设置Reset()方法。

AutoResetEvent如何工作的

在内存中保持着一个bool值,如果bool值为False,则使线程阻塞,反之,如果bool值为True,则使线程退出阻塞。当我们创建AutoResetEvent对象的实例时,我们在函数构造中传递默认的bool值。

AutoResetEvent autoResetEvent = new AutoResetEvent(false);
WaitOne 方法

该方法阻止当前线程继续执行,并使线程进入等待状态以获取其他线程发送的信号。WaitOne将当前线程置于一个休眠的线程状态。WaitOne方法收到Set信号后将返回True,否则将返回False。

WaiteOne方法的第一个重载版本是必须等到Set信号才会继续执行,否则将会一直等待下去。

autoResetEvent.WaitOne();

WaitOne方法的第二个重载版本是等待指定的秒数。如果在指定的秒数后,没有收到任何信号,方法返回false后,代码将继续执行。

autoResetEvent.WaitOne(TimeSpan.FromSeconds(2));
Set 方法

AutoResetEvent Set方法发送信号到等待线程以继续其工作,以下是调用该方法的格式。

autoResetEvent.Set();

实例

AutoResetEvent settlementEvent = new AutoResetEvent(false);
//主动结算线程
var initiative = new Thread(() =>
{
Thread.Sleep(30);
LogHelper.WriteInfo("主动结算线程完成");
settlementEvent.Set();
})
{
Name = $"{cabinet.PortName}订单主动结算进程"
};

//自动结算线程
var automatic = new Thread(() =>
{
Thread.Sleep(60);
LogHelper.WriteInfo("自动结算线程完成");
settlementEvent.Set();
})
{
Name = $"{cabinet.PortName}订单自动结算进程"
};
initiative.Start();
automatic.Start();
settlementEvent.WaitOne();
initiative.Abort();
automatic.Abort();

本方法创建了一个settlementEvent为订单AutoResetEvent的实例,分别创建了initiative为订单的主动结算进程,automatic为订单的自动结算线程。在线程中使用线程睡眠来模拟用户的操作。当某一个线程操作完成后触发settlementEvent订单信号量。以触发后销毁订单结算的所有线程防止重复结算。