上一篇介紹了經過lock關鍵字和Monitor類型進行線程同步,本篇中就介紹一下經過同步句柄進行線程同步。數組
在Windows系統中,可使用內核對象進行線程同步,內核對象由系統建立並維護。內核對象爲內核所擁有,因此不一樣進程能夠訪問同一個內核對象, 如進程、線程、事件、信號量、互斥量等都是內核對象。其中,信號量,互斥體,事件是Windows專門用來進行線程同步的內核對象。dom
在.NET中,有一個WaitHandle抽象類,這個類型封裝了一個Windows內核對象句柄,在C#代碼中,咱們就可使用WaitHandle類型(確切的說是子類型)的實例進行線程同步了。下面圖中顯示了WaitHandle類型的全部子類,下面對各個類型進行介紹。函數
WaitHandle類型中的SafeWaitHandle屬性就是咱們前面提到的Windows內核對象句柄,WaitHandle是一個抽象類,不能實例化。spa
下面看看WaitHandle中經常使用方法:線程
EventWaitHandle 類容許線程經過發信號互相通訊, 一般狀況下,一個或多個線程在 EventWaitHandle 上阻止,直到一個未阻止的線程調用 Set 方法,以釋放一個或多個被阻止的線程。3d
在EventWaitHandle類型中,除了父類中的方法,又有本身的特有方法,下面幾個是比較經常使用的:code
EventWaitHandle類型有兩個子類AutoResetEvent和ManualResetEvent,這兩個子類分別表明了EventWaitHandle類型對事件狀態的重置模式。在釋放單個等待線程後,用 EventResetMode.AutoReset 標誌建立的 EventWaitHandle 在終止時會自動重置; 用 EventResetMode.ManualReset 標誌建立的 EventWaitHandle 一直保持終止狀態,直到它的 Reset 方法被調用。對象
經過EventWaitHandle類型的構造函數,咱們能夠經過參數指定EventResetMode,從而選擇事件狀態的重置模式。固然,咱們也能夠直接使用EventWaitHandle的兩個子類。blog
public EventWaitHandle(bool initialState, EventResetMode mode); public enum EventResetMode { AutoReset = 0, ManualReset = 1, }
AutoResetEvent 容許線程經過發信號互相通訊:進程
能夠經過將一個布爾值傳遞給構造函數來控制 AutoResetEvent 的初始狀態:若是初始狀態爲終止狀態,則爲 true;不然爲 false。
下面看一個例子:
namespace AutoResetEventTest { class Program { //AutoResetEvent實例初始爲非終止狀態 private static AutoResetEvent autoResetEvent = new AutoResetEvent(false); static void Main(string[] args) { new Thread(() => { while (true) { //調用WaitOne來等待信號,並設置超時時間爲5秒 bool status = autoResetEvent.WaitOne(5000); if (status) { Console.WriteLine("ThreadOne get the signal"); } else { Console.WriteLine("ThreadOne timeout(5 seconds) waiting for signal"); break; } } Console.WriteLine("ThreadOne Exit"); }).Start(); new Thread(() => { while (true) { //調用WaitOne來等待信號,並設置超時時間爲5秒 bool status = autoResetEvent.WaitOne(5000); if (status) { Console.WriteLine("ThreadTwo get the signal"); } else { Console.WriteLine("ThreadTwo timeout(5 seconds) waiting for signal"); break; } } Console.WriteLine("ThreadTwo Exit"); }).Start(); Random ran = new Random(); for (int i = 0; i < 8; i++) { Thread.Sleep(ran.Next(500, 1000)); //經過Set向 AutoResetEvent 發信號以釋放等待線程 Console.WriteLine("Main thread send the signal"); autoResetEvent.Set(); } Console.Read(); } } }
代碼的輸出爲下,經過結果也能夠驗證,每次調用Set方法以後,AutoResetEvent 將保持終止狀態,直到一個正在等待的線程被釋放,而後自動返回非終止狀態。
像AutoResetEvent同樣,ManualResetEvent 也是線程經過發信號互相通訊:
能夠經過將布爾值傳遞給構造函數來控制 ManualResetEvent 的初始狀態,若是初始狀態處於終止狀態,爲 true;不然爲 false。
看一個例子:
namespace ManualResetEventTest { class Program { //ManualResetEvent實例初始爲非終止狀態 private static ManualResetEvent manualResetEvent = new ManualResetEvent(false); static void Main(string[] args) { new Thread(() => { //調用WaitOne來等待信號 manualResetEvent.WaitOne(); Console.WriteLine("Thread get the signal - the first time"); Thread.Sleep(1000); manualResetEvent.WaitOne(); Console.WriteLine("Thread get the signal - the second time"); //調用Reset來以將 ManualResetEvent 置於非終止狀態 Console.WriteLine("Child thread reset ManualResetEvent to non-signaled"); manualResetEvent.Reset(); manualResetEvent.WaitOne(); Console.WriteLine("Thread get the signal - the third time"); Console.WriteLine("Child thread reset ManualResetEvent to non-signaled"); manualResetEvent.Reset(); //調用WaitOne來等待信號,並設置超時時間爲3秒 manualResetEvent.WaitOne(3000); Console.WriteLine("timeout while waiting for signal"); }).Start(); //經過Set向 ManualResetEvent 發信號以釋放等待線程 Console.WriteLine("Main thread set ManualResetEvent to signaled"); manualResetEvent.Set(); Thread.Sleep(3000); Console.WriteLine("Main thread set ManualResetEvent to signaled"); manualResetEvent.Set(); Console.Read(); } } }
代碼的輸出以下,經過結果驗證了,每次調用Set都會將ManualResetEvent設置爲終止狀態,並釋放全部等待線程。只有手動調用 Reset才能將 ManualResetEvent 置於非終止狀態。
前一篇文章中介紹的lock和Monitor只能進行同一個進程中的線程同步。
可是,因爲同步事件EventWaitHandle是基於內核事件的,因此說,它能夠實現進程之間的線程同步。
基於前面AutoResetEvent的例子稍做修改:
class Program { private static EventWaitHandle eventWaitHandle; private static bool newEventWaitHandleObj = true; static void Main(string[] args) { string EventWaitHandleName = "EventWaitHandleTest"; try { //嘗試打開已有的同步事件 eventWaitHandle = EventWaitHandle.OpenExisting("EventWaitHandleTest"); newEventWaitHandleObj = false; } catch (WaitHandleCannotBeOpenedException e) { Console.WriteLine("EventWaitHandle named {0} is not exist, error message: {1}", EventWaitHandleName, e.Message); //實例化同步事件,初始爲非終止狀態,設置爲自動重置模式 eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "EventWaitHandleTest"); Console.WriteLine("Create EventWaitHandle {0}", EventWaitHandleName); newEventWaitHandleObj = true; } new Thread(() => { while (true) { //調用WaitOne來等待信號,並設置超時時間爲5秒 bool status = eventWaitHandle.WaitOne(5000); if (status) { Console.WriteLine("ThreadOne get the signal"); } else { Console.WriteLine("ThreadOne timeout(5 seconds) waiting for signal"); break; } } Console.WriteLine("ThreadOne Exit"); }).Start(); new Thread(() => { while (true) { //調用WaitOne來等待信號,並設置超時時間爲5秒 bool status = eventWaitHandle.WaitOne(5000); if (status) { Console.WriteLine("ThreadTwo get the signal"); } else { Console.WriteLine("ThreadTwo timeout(5 seconds) waiting for signal"); break; } } Console.WriteLine("ThreadTwo Exit"); }).Start(); if (newEventWaitHandleObj) { Random ran = new Random(); for (int i = 0; i < 8; i++) { Thread.Sleep(ran.Next(500, 1000)); //經過Set向 AutoResetEvent 發信號以釋放等待線程 Console.WriteLine("Main thread send the signal"); eventWaitHandle.Set(); } } Console.Read(); } }
代碼的輸出爲下,代碼中經過OpenExisting方法嘗試打開已存在的同步事件句柄,若是失敗,就建立一個EventWaitHandle實例。
至於後面部分代碼的工做原理,跟AutoResetEvent的例子徹底同樣。
接下來,咱們找到工程生成的exe文件,而後同時啓動兩次exe文件,能夠看到以下輸出,後面啓動的進程可以打開前面進程建立的同步事件句柄。經過這種方式,就能夠實現進程之間的線程同步。
本文介紹了WaitHandle類型,以及該類型的子類型EventWaitHandle,而且介紹瞭如何經過AutoResetEvent和ManualResetEvent進行線程同步。
下一篇將繼續介紹互斥體Mutex和信號量Semaphore的使用。