在多線程開發中,時常用到 ManualResetEvent 與 AutoResetEvent 。 它們如同道路交通中的信號燈。兩者之間有什么區別呢?
共同點:
均繼承 EventWaitHandle 接口,因此,均具有以下功能:
Reset() //紅燈
Set() //綠燈
WaitOne() // 等待信號
不同點:
AutoResetEvent 收到 Set 后 , 一次只能執行一個線程,其它線程繼續 WaitOne 。
ManualResetEvent 收到 Set 后,所有處理 WaitOne 狀態線程均繼續執行。
msdn 提到(如果沒有線程 處於WaitOne() 狀態,而調用 Set , AutoResetEvent 將保持Set 狀態):
調用Set信號AutoResetEvent釋放等待線程。 AutoResetEvent 將保持終止狀態直到一個等待線程釋放,並自動返回到非信號狀態。 如果沒有線程處於等待狀態,狀態將無限期地保持已發出信號。
因此通常WatiOne 之前,先 Reset() 一下,清除Set 信號
需要注意的是(兩個 Set 調用之間時間較短,第二個 Set 信號可能會丟失,因此連續 Set 調用,中間需要 Sleep 一定時間):
不能保證的每個調用Set方法將釋放一個線程。 如果兩次調用太靠近在一起,以便第二次調用前釋放線程發生,只有一個線程被釋放。 就像第二次調用未發生。 此外,如果Set時沒有等待的線程調用和AutoResetEvent已終止,則調用不起作用。
有網友說:
AutoResetEvent.Set() = ManualResetEvent.Set() + ManualResetEvent.Reset();
個人理解 ,這只是原理層面含義,實際使用過程中,有差別的,如下示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace testManualResetEvent { class Program { static object objManualResetEvent = new object(); static System.Threading.ManualResetEvent manu = new System.Threading.ManualResetEvent(false); //static System.Threading.AutoResetEvent manu = new System.Threading.AutoResetEvent(false); static void Main(string[] args) { for (int i = 0; i < 10; i++) { System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(() => { Product(); })); t.Start(); } manu.Set(); manu.Reset(); Console.ReadKey(); } static void Product() { manu.WaitOne(10000); Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); } } }
實際執行結果 , 在 執行 set 后 reset 前 ,有多少個線程喚起執行,無法預料:
需要加鎖 ,確保一次通過一個線程:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace testManualResetEvent { class Program { static object objManualResetEvent = new object(); static System.Threading.ManualResetEvent manu = new System.Threading.ManualResetEvent(false); //static System.Threading.AutoResetEvent manu = new System.Threading.AutoResetEvent(false); static void Main(string[] args) { for (int i = 0; i < 10; i++) { System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(() => { Product(); })); t.Start(); } manu.Set(); //System.Threading.Thread.Sleep(100); //連續 set 需要 sleep //manu.Set(); //manu.Reset(); //System.Threading.Thread.Sleep(100); //manu.Set(); //manu.Reset(); Console.ReadKey(); } static void Product() { lock (objManualResetEvent) { manu.WaitOne(10000);
manu.Reset(); Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); } } } }
執行結果: