多线程编程中的EventWaitHandler

首先如果读者是.Net多线程编程的老手,就不用看这篇文章了,这篇文章主要是阐述EventWaitHandler的一些基本原理和用法。  

 

在.NET的System.Threading命名空间中有一个名叫WaitHandler的类,这是一个抽象类(abstract),我们无法手动去创建它,但是WaitHandler有三个子类,这三个子类分别是:System.Threading.EventWaitHandle,System.Threading.Mutex,System.Threading.Semaphore,这三个类都是非Abstract的,可以由开发者来创建和使用,其中本文主要讨论的是其中的System.Threading.EventWaitHandle类。

 

EventWaitHandle类的用途是可以调用其WaitOne方法来阻塞线程的运行,直到得到一个信号(该信号由EventWaitHandle类的Set方法发出),然后释放线程让其不再阻塞继续运行。

 

EventWaitHandle类拥有两种状态,终止状态非终止状态

  • 在终止状态下,被WaitOne阻塞的线程会逐个得到释放,所以当EventWaitHandle始终处于终止状态时,调用其WaitOne方法无法起到阻塞线程的作用,因为线程被其WaitOne方法阻塞后,会立即被释放掉。
  • 在非终止状态下,被WaitOne阻塞的线程会继续被阻塞,如果一个线程在EventWaitHandle对象处于非终止状态时调用了其WaitOne函数,该线程会立即被阻塞。

需要注意的是终止状态和非终止状态之间,是可以相互转换的。调用EventWaitHandle对象的Set方法既可以将EventWaitHandle对象设置为终止状态,调用EventWaitHandle对象的Reset方法既可以将EventWaitHandle对象设置为非终止状态。

 

此外,EventWaitHandle类还拥有两种模式,AutoResetManualReset 模式:

  • 在AutoReset模式下,当EventWaitHandle对象被置为终止状态时,释放一个被WaitOne阻塞的线程后,EventWaitHandle对象会马上被设置为非终止状态,这个过程就等同于一个被WaitOne阻塞的线程被释放后,自动调用了EventWaitHandle的Reset方法,将EventWaitHandle对象自动从终止状态置回了非终止状态,所以这种模式叫AutoReset模式。所以如果有若干线程被EventWaitHandle对象的WaitOne方法阻塞了,每调用一次EventWaitHandle对象的Set方法将EventWaitHandle对象置为终止状态后,只能释放一个被阻塞的线程,然后EventWaitHandle对象又会被置为非终止状态。如果EventWaitHandle对象的Set方法之后又被调用了一次,剩下那些被阻塞的线程中,又会有一个线程被释放。所以如果有8个被WaitOne方法阻塞的线程,那么需要调用次EventWaitHandle对象的Set方法8次,才能让所有线程都得到释放。需要注意的一点就是MSDN中有提到:如果两次EventWaitHandle对象的Set方法调用非常接近,以至于当第一次调用Set方法后,被阻塞的线程还没有来得及释放,第二次Set调用又开始了,那么这两次Set方法的调用只会让一个被阻塞的线程被释放,也就是说如果两次Set方法的调用过于接近,那么就相当于只调用了一次。原因就是因为由于两次Set调用过于接近,当第一次Set调用后,其释放的线程还没有完全被释放,即EventWaitHandle对象还没有被置回非终止状态,第二次Set调用又开始了,又要求EventWaitHandle对象变成终止状态去释放剩余的阻塞线程,但是问题是现在EventWaitHandle对象本来就处于终止状态,并且第一次Set调用后的那个被释放的线程还没有被完全释放,所以现在不能去释放剩余的阻塞线程。之后待第一次Set调用后的那个被释放线程完全释放后,由于EventWaitHandle对象处于AutoReset模式,所以现在EventWaitHandle对象才会被置回非终止状态,那么就相当于第二次Set调用就白白浪费了一次机会去将EventWaitHandle对象置为终止状态去释放剩余的阻塞线程。
  • 在ManualReset模式下,当EventWaitHandle对象被置为终止状态时,释放一个被WaitOne阻塞的线程后,其状态不会改变,仍然处于终止状态,所以当ManualReset模式下EventWaitHandle对象处于终止状态时,会连续释放所有被WaitOne方法阻塞的线程,直到手动调用其Reset方法将其置回非终止状态。所以这种模式叫ManualReset模式。

 

使用EventWaitHandle类的构造函数可以设置EventWaitHandle对象的模式和初始状态,以下是EventWaitHandle类的其中一个构造函数:

public EventWaitHandle
(
     bool initialState,
    EventResetMode mode
)
  • 第一个参数initialState为true时,表示EventWaitHandle对象的初始状态为终止状态,反之false表示EventWaitHandle对象的初始状态为非终止状态。
  • 第二个参数EventResetMode mode为EventResetMode.AutoReset时,表示EventWaitHandle对象处于AutoReset模式,当第二个参数为EventResetMode.ManualReset时,表示EventWaitHandle对象处于ManualReset模式。

需要注意的是,当EventWaitHandle对象的初始状态为终止状态,模式为AutoReset时,第一个被EventWaitHandle对象的WaitOne方法阻塞的线程会立即被释放,然后EventWaitHandle对象被置为非终止状态,如果后面还有线程被EventWaitHandle对象的WaitOne方法阻塞,就只有等到EventWaitHandle对象的Set方法调用,才能被释放了。原因我就不多说了,如果你还不明白请回去看上面对EventWaitHandle的终止状态和AutoReset模式的阐述。

 

下面是EventWaitHandle类的一个综合示例:

using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EventWaitHandleDemo
{
     class Program
    {
         static EventWaitHandle eHandle;

         static  void UnblockDemo()
        {
            Console.WriteLine( " 测试EventWaitHandle的初始终止状态 ");
            eHandle =  new EventWaitHandle( true, EventResetMode.AutoReset); // eHandle初始为终止状态,模式为AutoReset
            eHandle.WaitOne(); // 由于EventWaitHandle对象eHandle初始状态为终止状态,所以这里第一次调用WaitOne时阻塞被立即释放,又由于eHandle为AutoReset模式,所以之后eHandle会被置为非终止状态
            Console.WriteLine( " 线程未被阻塞 ");
            eHandle.WaitOne(); // 由于此时eHandle已经为非终止状态,所以此时调用WaitOne线程会被阻塞
            Console.WriteLine( " 线程被阻塞 ");
        }

         static  void BlockDemo()
        {
            Console.WriteLine( " 测试EventWaitHandle的初始非终止状态 ");
            eHandle =  new EventWaitHandle( false, EventResetMode.AutoReset); // eHandle初始为非终止状态,模式为AutoReset
            eHandle.WaitOne(); // 由于EventWaitHandle对象eHandle初始状态为非终止状态,所以这里第一次调用WaitOne时,线程就被组塞了
            Console.WriteLine( " 线程被阻塞 ");
        }

         static  void AutoResetDemo()
        {
            Console.WriteLine( " 测试EventWaitHandle的AutoReset模式 ");
            eHandle =  new EventWaitHandle( false, EventResetMode.AutoReset); // eHandle初始为非终止状态,模式为AutoReset
            ThreadPool.QueueUserWorkItem( new WaitCallback(( object o) =>
            {
                 // 启动另一个线程,每隔3秒钟调用一次eHandle.Set方法,为主线程释放一次阻塞,一共释放3次
                 for ( int i =  0; i <  3; i++)
                {
                    Thread.Sleep( 3000);
                    eHandle.Set(); // 由于eHandle处于AutoReset模式,所以每次使用Set将eHandle置为终止状态后,待被WaitOne阻塞的线程被释放后,eHandle又会被自动置回非终止状态
                }
            }),  null);

            eHandle.WaitOne(); // 线程第一次被WaitOne阻塞
            Console.WriteLine( " 第一次WaitOne调用阻塞已被释放,3秒后第二次WaitOne调用的阻塞会被释放 ");
            eHandle.WaitOne(); // 线程第二次被WaitOne阻塞
            Console.WriteLine( " 第二次WaitOne调用阻塞已被释放,3秒后第三次WaitOne调用的阻塞会被释放 ");
            eHandle.WaitOne(); // 线程第三次被WaitOne阻塞
            Console.WriteLine( " 第三次WaitOne调用阻塞已被释放,所有WaitOne调用的阻塞都已被释放 ");
        }

         static  void ManualResetDemo()
        {
            Console.WriteLine( " 测试EventWaitHandle的ManualReset模式 ");
            eHandle =  new EventWaitHandle( false, EventResetMode.ManualReset); // eHandle初始为非终止状态,模式为ManualReset
            ThreadPool.QueueUserWorkItem( new WaitCallback(( object o) =>
            {
                 // 启动另一线程,3秒后调用一次eHandle.Set方法,为主线程释放WaitOne阻塞
                Thread.Sleep( 3000);
                eHandle.Set(); // 由于eHandle处于ManualReset模式,所以一旦使用Set将eHandle置为终止状态后,在eHandle的Reset被调用前eHandle会一直处于终止状态,在eHandle调用Reset前,所有被WaitOne阻塞的线程会立即得到释放
            }),  null);

            eHandle.WaitOne(); // 线程第一次被WaitOne阻塞
            Console.WriteLine( " 第一次WaitOne调用阻塞已被释放,第二次WaitOne调用的阻塞会被立即释放 ");
            eHandle.WaitOne(); // 线程第二次被WaitOne阻塞
            Console.WriteLine( " 第二次WaitOne调用阻塞已被释放,第三次WaitOne调用的阻塞会被立即释放 ");
            eHandle.WaitOne(); // 线程第三次被WaitOne阻塞
            Console.WriteLine( " 第三次WaitOne调用阻塞已被释放,所有WaitOne调用的阻塞都已被释放 ");

            eHandle.Reset(); // 调用eHandle的Reset方法,将eHandle手动置回非终止状态,之后再调用WaitOne方法就会被阻塞了
            eHandle.WaitOne(); // 线程第四次被WaitOne阻塞
            Console.WriteLine( " 第四次WaitOne调用阻塞已被释放 ");
        }

         static  void Main( string[] args)
        {
            Console.Write( " 你想测试哪一个方法1=UnblockDemo,2=BlockDemo,3=AutoResetDemo,4=ManualResetDemo: ");
             switch (Console.ReadLine())
            {
                 case  " 1 ":
                    UnblockDemo();
                     break;
                 case  " 2 ":
                    BlockDemo();
                     break;
                 case  " 3 ":
                    AutoResetDemo();
                     break;
                 case  " 4 ":
                    ManualResetDemo();
                     break;
                 default:
                     break;
            }
        }
    }
}

 

最后要提一下,那就是EventWaitHandle类还有两个子类: System.Threading.AutoResetEvent类 和 System.Threading.ManualResetEvent类:

AutoResetEvent类和EventWaitHandle类处于AutoReset模式时类似

ManualResetEvent类和EventWaitHandle类处于ManualReset模式时类似

这两个类和EventWaitHandle类的用法几乎相同,所以这里就不多说了。

 

转载于:https://www.cnblogs.com/OpenCoder/archive/2011/12/29/2305528.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值