首先若是讀者是.Net多線程編程的老手,就不用看這篇文章了,這篇文章主要是闡述EventWaitHandler的一些基本原理和用法。 html
在.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類還擁有兩種模式,AutoReset 和 ManualReset 模式:spa
- 在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模式的闡述。code
下面是EventWaitHandle類的一個綜合示例:htm
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