好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

AutoResetEvent和ManualResetEvent的理解

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的理解的详细内容...

  阅读:48次