在情景一、情景二中,我分别介绍了当多线程遇到 “资源争用”、“限量使用” 情形时的解决方案,本篇是本系列的最后一种情形,会介绍几种用于解决线程通讯的方案。html
情景三:我让你动,你才能动!多线程
大锤:“老板,拿这个手机让我看看”。post
大锤:“这是手机吗??? 分别就只是一个壳子”。性能
老板:“呀,这多是生产上出了问题,我给你换一个!”spa
大锤:“老板,你这是当我是傻子呢?仍是傻子呢?仍是傻子呢? 这回给个人手机怎么没有电源啊!我要怎么开机啊!”操作系统
万万没想到,通过千挑万选,最终仍是找到了一个配件完整的手机。线程
老板回去后发现了缘由是:生产和上线销售两个环节没有搭配好,当生产的环节尚未结束时,就把中间产物拿去销售了。code
解决办法:全部动做不能擅自执行,必须服从命令,当生产环节完成时会通知上线环节,而后才被容许拿到市场上去销售。htm
问题抽象:当某个操做的执行必须依赖于另外一个操做的完成时,须要有个机制来保证这种前后关系。对象
线程通讯方案:ManualResetEventSlim、ManualResetEvent、AutoResetEvent
方案特性:提供线程通知的能力,没有接到通知前,线程必须等待,有前后顺序。
各方案间的区别
在继续阅读前,请确保你已经对用户模式构造、内核模式构造和混合模式构造有所了解,若是还没有了解,建议您先阅读情景一中相关章节。
ManualResetEvent 和 AutoResetEvent 都继承自 EventWaitHandle 并最终与 Mutex 和 Semaphore 同样拥有共同的祖宗: WaitHandle。在前面几篇中我有讲过 WaitHandle 是一个抽象类,包装了 Windows 操做系统的内核对象句柄。
ManualResetEvent: 中文理解就是手动重置事件(这里的事件并非咱们一般意义中按钮的那种事件,更多的应该理解为一个通告)。全部事件的初始状态都为 “不可用”,任何在等待该事件的线程都将一直等待下去。只有当经过 Set 方法释放了一个信号后,等待该事件的线程才被容许执行所需的操做。若是不手动重置,那么状态一直为 “可用”,任何等待该事件的线程能够继续执行操做。只有当调用 Reset 方法后,状态才会变为 “不可用”。经过 WaitOne 来请求状态,从而决定是否执行操做。
这个特色,有点相似红绿灯,当绿灯亮起,全部机动车都被容许经过,直到再次变成红灯。
与 Mutex 不一样的是,Reset 和 Set 操做能够由不一样的线程发起。如:
ManualResetEvent s = new ManualResetEvent(false); Task.Factory.StartNew(() => { s.Set(); }); Task.Factory.StartNew(() => { s.Reset(); }); Task.Factory.StartNew(() => { s.WaitOne(); });
优势:提供线程间通讯的能力,能够跨进程使用。
缺点:速度慢于用户模式、混合模式构造,稍快于 mutex。
AutoResetEvent: 顾名思义,就是自动重置事件。若是 ManulResetEvent 至关于红绿灯,那 AutoResetEvent 就相似高速入口的闸机,杆抬起一次,经过一辆车。第二辆车要从新等待杆抬起。当调用 Set 后,状态变成 “可用”,但只要一执行 WaitOne 请求状态后,状态便可变成 “不可用”(这个过程不须要 Reset 方法的参与)。
优势:提供线程间通讯的能力,能够跨进程使用。
缺点:速度慢于用户模式、混合模式构造,稍快于 mutex。
在 .Net 4.0 时候引入了 ManualResetEventSlim 来提升性能。下面是 MSDN 的原话:
在 .NET Framework 4 版中,当等待时间预计很是短时,而且当事件不会跨越进程边界时,可以使用 System.Threading.ManualResetEventSlim 类以得到更好的性能。当等待事件变为已发出信号状态的过程当中,ManualResetEventSlim 短期内会使用繁忙旋转。 当等待时间很短时,旋转的开销相对于使用等待句柄来进行等待的开销会少不少。 可是,若是事件在某个时间段内没有变为已发出信号状态,则 ManualResetEventSlim 会采用常规的事件处理等待。
ManualResetEventSlim 的用法与 ManualResetEvent 几乎类似,只是原先使用 WaitOne 的地方须要使用 Wait 代替。
优势:提供线程间通讯的能力。
缺点:不能跨进程使用,速度快于内核模式构造。
本篇文章所解决的是当两个或多个线程之间须要按某种顺序执行的时候,线程间的同步问题。若是在开发中遇到两个线程须要按某种顺序前后执行的,则应该考虑使用 ManualResetEvent 或 AutoResetEvent。
本文来自《C# 基础回顾: 线程同步的情景之三》