AutoResetEvent和ManualResetEvent的理解
一、作用
AutoResetEven t和 ManualResetEvent 可用于控制线程暂停或继续,拥有重要的三个方法: WaitOne 、 Set 和 Reset 。
这三个方法的官方定义并不好理解,什么终止、非终止,乱七八糟的。在这里,我们以一种通俗易懂的概念来说明。
二、比喻
如果把每个线程比作一辆汽车的话, AutoResetEvent 和 ManualResetEvent 就是公路上的收费站。
其中,
Reset 关闭收费站车闸禁止通行(拦截车辆才好收费啊);
WaitOne 收费员等待下一辆车辆过来(然后收费);
Set 开启收费站车闸放行(交钱了就让过去)。
三、AutoResetEvent和ManualResetEvent的区别
既然 AutoResetEvent 和 ManualResetEvent 都是收费站,那么它们之间有什么不同之处吗?
顾名思义, Auto 即自动, Manual 即手动,而 Reset 根据上面的比喻表示关闭车闸,也就是前者可自动关闭车闸,后者需手动关闭车闸。
自动关闭车闸:即一辆车交钱通过后,车闸会自动关闭,然后再等待下一辆车过来交费。即每辆车都要经过这么几个步骤:被阻 > 交费 > 通行 > 车闸关闭
手动关闭车闸:车闸打开后,车闸不会自动关闭,如果不手动关闭车闸(即调用 ManualResetEvent.Reset() 方法)的话,车辆会一辆接一辆地通过……
所以 WaitOne 收费操作取决于车闸是否关闭( Reset ),如果车闸是开启的, WaitOne 的收费愿望只能落空,收费站形同虚设。
四、AutoResetEvent和ManualResetEvent的初始状态
通过设置 AutoResetEvent 和 ManualResetEvent 构造函数可初始化收费站车闸状态:
new Auto/ManualResetEvent( false ) : 车闸默认关闭;
new Auto/ManualResetEvent( true ) : 车闸默认开启。
如果 new Auto/ManualResetEvent( true ) ,即车闸默认开启的话, WaitOne 没任何意义,车辆该通过还通过。
看下面代码:
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( " 噫!车闸是开的,我过来了! " ); }
运行将打印:
噫!车门是开的,我过来了!
如果将 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 , 0 , 2000 ); // 2秒后关闭车闸 Console.ReadKey(); } static void Car1() { _tollStation.WaitOne(); // 等待开启车闸,即_tollStation.Set(); Console.WriteLine( " 车辆1,顺利通过。 " ); } static void Car2() { Thread.Sleep( 3000 ); // 睡眠3秒 _tollStation.WaitOne(); // 当醒来后车闸已经被关闭 Console.WriteLine( " 车辆2,顺利通过。 " ); // 所以车辆2不会被通过 } /// <summary> /// 2秒后关闭车闸 /// </summary> static void CloseDoor( object o) { _tollStation.Reset(); // 关闭车闸 }
运行将打印:
车辆1,顺利通过。
而车辆2将不会通过,因为当车辆2醒来时,车闸在2秒前已被关闭。
七、总结
1、看起来, ManualResetEvent 更加自由、开放。如果把 AutoResetEvent 看作是只能单人通过的独木桥的话,那么 ManualResetEvent 就像一座城门,一下子可以涌入千军万马,当然你也可以随时关闭城门,让后面的人进不来。
2、 AutoResetEvent.Set() = ManualResetEvent.Set() + ManualResetEvent.Reset() ;
3、如果共享资源仅允许一个线程单独使用的情况下,可以选择 AutoResetEvent ;如果共享资源允许多个线程同时使用,则可以选择 ManualResetEvent 。
4、如果要控制多个线程暂停、继续,可以选择 ManualResetEvent 。
5、等您来补充……
分类: C#
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于AutoResetEvent和ManualResetEvent的理解的详细内容...