摘自风中灵药的博客:https://www.cnblogs.com/qingyun163/archive/2013/01/05/2846633.html#!comments

AutoResetEvent和ManualResetEvent可用于控制线程暂停或继续,拥有重要的三个方法:WaitOneSetReset

如果把每个线程比作一辆汽车的话,AutoResetEventManualResetEvent就是公路上的收费站。

其中:

Reset 关闭收费站车闸禁止通行(拦截车辆才好收费啊);

WaitOne 收费员等待下一辆车辆过来(然后收费);

Set    开启收费站车闸放行(交钱了就让过去)。

AutoResetEvent和ManualResetEvent的区别

既然AutoResetEventManualResetEvent都是收费站,那么它们之间有什么不同之处吗?

顾名思义,Auto即自动,Manual即手动,而Reset根据上面的比喻表示关闭车闸,也就是前者可自动关闭车闸,后者需手动关闭车闸。

自动关闭车闸:即一辆车交钱通过后,车闸会自动关闭,然后再等待下一辆车过来交费。即每辆车都要经过这么几个步骤:被阻 > 交费 > 通行 > 车闸关闭

手动关闭车闸:车闸打开后,车闸不会自动关闭,如果不手动关闭车闸(即调用ManualResetEvent.Reset()方法)的话,车辆会一辆接一辆地通过……

AutoResetEvent和ManualResetEvent的初始状态

通过设置AutoResetEventManualResetEvent构造函数可初始化收费站车闸状态:

new Auto/ManualResetEvent(false)车闸默认关闭;
new Auto/ManualResetEvent(true) 车闸默认开启。

如果new Auto/ManualResetEvent(true),即车闸默认开启的话,是默认让第一辆车通过,然后会拦截下一辆车。

static EventWaitHandle _tollStation = new AutoResetEvent(true);//车闸默认开启

        static void Main(string[] args)
{
new Thread(Car1).Start();
Console.ReadKey();
} static void Car1()
{
_tollStation.WaitOne();//因车闸默认开启,WaitOne毫无意义,不会阻止车辆前行
Console.WriteLine("噫!车闸是开的,我过来了!");
}

此时输出:"噫!车闸是开的,我过来了!"

但是如果改一下

static EventWaitHandle _tollStation = new AutoResetEvent(true);//车闸默认开启

        static void Main(string[] args)
{
new Thread(Car1).Start();
new Thread(Car1).Start();//执行两次输出的线程
Console.ReadKey();
} static void Car1()
{
_tollStation.WaitOne();//因车闸默认开启,WaitOne毫无意义,不会阻止车辆前行
Console.WriteLine("噫!车闸是开的,我过来了!");
}

此时输出:噫!车闸是开的,我过来了!

执行两次线程,但是只会输出一次。因为第二次被阻拦了。

如果将new AutoResetEvent(true) 改为new AutoResetEvent(flase),即车闸默认为关闭状态的话,将不会打印任何值,即车辆无法通过。

那如何才能通过呢?必须在主线程中调用Set方法,即打开车闸即可通过。

static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭

        static void Main(string[] args)
{
new Thread(Car1).Start();
_tollStation.Set();//开启车闸
Console.ReadKey();
} static void Car1()
{
_tollStation.WaitOne();//等待开启车闸,即_event.Set();
Console.WriteLine("车闸开启,我过来了!");
}

运行将打印:

车闸开启,我过来了!

代码很明了,就不解释了,总之就是车闸默认关闭状态下,只有打开车闸(调用Set方法 ),车辆才能通行。

用代码阐释AutoResetEvent的特性

static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭

        static void Main(string[] args)
{
new Thread(Car1).Start();//车辆1
new Thread(Car2).Start();//车辆2
_tollStation.Set();
Console.ReadKey();
} static void Car1()
{
_tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
Console.WriteLine("车辆1,顺利通过。");
} static void Car2()
{
_tollStation.WaitOne();
Console.WriteLine("车辆2,顺利通过。!");
}

运行将打印:

车辆1,顺利通过。

虽然车辆1和车辆2都在运行,但只有车辆1顺利通过。

因为_tollStation.Set()仅运行了一次,即车辆1通过后车闸被立即关闭,导致车辆2未被通过。

除非,在车辆1通过后再调用一次_tollStation.Set(),即再次打开车闸,车辆2才能通过:

static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭

        static void Main(string[] args)
{
new Thread(Car1).Start();//车辆1
new Thread(Car2).Start();//车辆2
_tollStation.Set();//开启车闸,让车辆1通过
Console.ReadKey();
} static void Car1()
{
_tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
Console.WriteLine("车辆1,顺利通过。");
_tollStation.Set();//再开启一次车闸,让车辆2通过
} static void Car2()
{
_tollStation.WaitOne();
Console.WriteLine("车辆2,顺利通过。");
}

运行将打印:

车辆1,顺利通过。

车辆2,顺利通过。

也就是每调用一次Set,仅有一个线程会继续。换言之,有多少个线程就要调用多少次Set,线程才会全部继续。

这也表明,AutoResetEvent是典型的队列操作形式。

用代码阐释ManualResetEvent的特性

在上一个代码块中,_tollStation.Set()调用了两次,两辆车才顺利通过。

那么,有没有什么办法,只调用一次_tollStation.Set()就让两辆或更多辆汽车顺利通过呢?

答案是,将AutoResetEvent改为ManualResetEvent

static EventWaitHandle _tollStation = new ManualResetEvent(false);//改为ManualResetEvent,车闸默认关闭

        static void Main(string[] args)
{
new Thread(Car1).Start();//车辆1
new Thread(Car2).Start();//车辆2
_tollStation.Set();//开启车闸,所有车辆都会通过
Console.ReadKey();
} static void Car1()
{
_tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
Console.WriteLine("车辆1,顺利通过。");
//_tollStation.Set();//这里不再需要了
} static void Car2()
{
_tollStation.WaitOne();
Console.WriteLine("车辆2,顺利通过。");
}

运行将打印:

车辆1,顺利通过。

车辆2,顺利通过。

这很好的说明了,ManualResetEvent开启车闸后不会自动关闭这一特性。所以调用一次_tollStation.Set(),全部车辆将顺利通过。

如果在某一时刻手动关闭了车闸,则后面的车辆将无法通过。如以下代码:

static EventWaitHandle _tollStation = new ManualResetEvent(false);//改为ManualResetEvent,车闸默认关闭

        static void Main(string[] args)
{
new Thread(Car1).Start();//车辆1
new Thread(Car2).Start();//车辆2 _tollStation.Set();//开启车闸,放行
Timer timer = new Timer(CloseDoor, null, , );//2秒后关闭车闸 Console.ReadKey();
} static void Car1()
{
_tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
Console.WriteLine("车辆1,顺利通过。");
} static void Car2()
{
Thread.Sleep();//睡眠3秒
_tollStation.WaitOne();//当醒来后车闸已经被关闭
Console.WriteLine("车辆2,顺利通过。");//所以车辆2不会被通过
} /// <summary>
/// 2秒后关闭车闸
/// </summary>
static void CloseDoor(object o)
{
_tollStation.Reset();//关闭车闸
}

运行将打印:

车辆1,顺利通过。

而车辆2将不会通过,因为当车辆2醒来时,车闸在2秒前已被关闭。

 

最新文章

  1. wf(六)
  2. day 2 常用快捷键
  3. webpack中output配置项中chunkFilename属性的用法
  4. IIS7.0+部署ARR负载均衡
  5. AEM - Adobe CMS 扒坑记之始
  6. 你能不用计算机来计算S=a+(a+1)+(a+2) + ...... + b的解的数目吗?
  7. BeanUtils.copyProperties() 用法--部分转
  8. sql给数据库加锁问题
  9. Linux下动态调整LVM文件系统大小
  10. 对 Xcode 菜单选项的详细探索(转)
  11. Kyoya and Colored Balls(组合数)
  12. 什么是DNS劫持和DNS污染?
  13. OO第二单元的总结
  14. THINKphp中常见的Request请求类
  15. linux下ssh远程连接工具SecureCRT和xshell编码设置
  16. 集合(4)—Collection之Set的使用方法
  17. Resharper插件的使用
  18. RGB,YCBCR在HDMI传输线是数据排列
  19. javaSrript_数据类型(2017-03-15)
  20. hadoop完全分布式搭建HA(高可用)

热门文章

  1. 配置lnmp时出现“file not found?
  2. QTP(7)
  3. shodan使用
  4. CF #546 D.E
  5. ribbon负载均衡循环策略源码
  6. KMP算法详解&amp;&amp;P3375 【模板】KMP字符串匹配题解
  7. substring和substr的区别
  8. mysql解除锁表
  9. li元素之间产生间隔
  10. ssh2整合: No bean named &#39;sessionFactory&#39; is defined(求大神指点)