线程同步的情景之三

简介:

 在情景一情景二中,我分别介绍了当多线程遇到 “资源争用”、“限量使用” 情形时的解决方案,本篇是本系列的最后一种情形,会介绍几种用于解决线程通信的方案。

 

 情景三:我让你动,你才能动!

 

  大锤:“老板,拿这个手机让我看看”。

  大锤:“这是手机吗??? 分别就只是一个壳子”。

  老板:“呀,这可能是生产上出了问题,我给你换一个!”

  大锤:“老板,你这是当我是傻子呢?还是傻子呢?还是傻子呢? 这回给我的手机怎么没有电源啊!我要怎么开机啊!”

  万万没想到,经过千挑万选,最终还是找到了一个配件完整的手机。

 

  老板回去后发现了原因是:生产和上线销售两个环节没有搭配好,当生产的环节还没有结束时,就把中间产物拿去销售了。

  解决办法:所有动作不能擅自执行,必须服从命令,当生产环节完成时会通知上线环节,然后才被允许拿到市场上去销售。

  问题抽象:当某个操作的执行必须依赖于另一个操作的完成时,需要有个机制来保证这种先后关系。

  线程通信方案:ManualResetEventSlim、ManualResetEvent、AutoResetEvent

  方案特性:提供线程通知的能力,没有接到通知前,线程必须等待,有先后顺序。

 

  各方案间的区别 

  在继续阅读前,请确保你已经对用户模式构造、内核模式构造和混合模式构造有所了解,如果尚未了解,建议您先阅读情景一中相关章节。

 

内核模式(kernal-mode)

  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。

 

混合模式(hybrid-mode)

  在 .Net 4.0 时候引入了 ManualResetEventSlim 来提高性能。下面是 MSDN 的原话:

在 .NET Framework 4 版中,当等待时间预计非常短时,并且当事件不会跨越进程边界时,可使用 System.Threading.ManualResetEventSlim 类以获得更好的性能。当等待事件变为已发出信号状态的过程中,ManualResetEventSlim 短时间内会使用繁忙旋转。 当等待时间很短时,旋转的开销相对于使用等待句柄来进行等待的开销会少很多。 但是,如果事件在某个时间段内没有变为已发出信号状态,则 ManualResetEventSlim 会采用常规的事件处理等待。

--- 《ManualResetEvent 和 ManualResetEventSlim》

  ManualResetEventSlim 的用法与 ManualResetEvent 几乎相似,只是原先使用 WaitOne 的地方需要使用 Wait 代替。

 

  优点:提供线程间通信的能力。

  缺点:不能跨进程使用,速度快于内核模式构造。

 

总 结

  本篇文章所解决的是当两个或多个线程之间需要按某种顺序执行的时候,线程间的同步问题。如果在开发中遇到两个线程需要按某种顺序先后执行的,则应该考虑使用 ManualResetEvent 或 AutoResetEvent。






本文转自stg609博客园博客,原文链接:http://www.cnblogs.com/stg609/p/4052008.html,如需转载请自行联系原作者

目录
相关文章
|
1月前
|
Shell Python
Python多线程怎么做?
Python 3 中利用 `threading` 模块实现多线程。创建与执行线程有两种常见方式:一是直接使用 `Thread` 类实例,指定目标函数;二是通过继承 `Thread` 类并重写 `run` 方法。前者构造 `Thread` 对象时通过 `target` 参数指定函数,后者则在子类中定义线程的行为。两种方式均需调用 `start` 方法启动线程。示例展示了这两种创建线程的方法及输出顺序,体现线程并发执行的特点。
|
9月前
|
存储 安全 Java
Java并发编程学习4-线程封闭和安全发布
本篇介绍 对象的共享之线程封闭和安全发布
75 2
Java并发编程学习4-线程封闭和安全发布
|
安全 算法 Java
【并发编程技术】「技术辩证分析」在并发编程模式下进行线程安全以及活跃性问题简析
【并发编程技术】「技术辩证分析」在并发编程模式下进行线程安全以及活跃性问题简析
69 0
【并发编程技术】「技术辩证分析」在并发编程模式下进行线程安全以及活跃性问题简析
二十、经典同步问题-读者写者问题
二十、经典同步问题-读者写者问题
|
安全 Java 调度
Android多线程编程——线程基础
Android沿用了Java的线程模型,一个Android应用在创建的时候会开启一个线程,我们叫它主线程或者UI线程。
234 0
Android多线程编程——线程基础
|
存储 SQL 安全
搞定|通过实际案例搞懂多任务线程
搞定|通过实际案例搞懂多任务线程
搞定|通过实际案例搞懂多任务线程
|
存储 缓存 监控
Java并发编程系列之二线程基础
上篇文章对并发的理论基础进行了回顾,主要是为什么使用多线程、多线程会引发什么问题及引发的原因,和怎么使用Java中的多线程去解决这些问题。
Java并发编程系列之二线程基础
|
安全 C++
模拟生产者-消费者问题和读者-写者问题
模拟生产者-消费者问题和读者-写者问题
352 0
模拟生产者-消费者问题和读者-写者问题
|
Java 调度
Java并发编程程系列之二:多线程实现的三种方式
多线程处理是Java中处理并发任务非常重要的手段。本文主要介绍了多线程实现的几种方式以及每种实现方式优缺点,以供大家在实际开发中可以根据实际的应用场景进行自由选择。 继承Thread 实现Runnable接口 实现Callable接口
Java并发编程程系列之二:多线程实现的三种方式