一、簡介
我們使用類(.net Framework中的類,如 AutoResetEvent, Semaphore類等)的方法來實現線程同步的時候,其實內部是調用操作系統的內核對象來實現的線程同步。
System.Threading命名空間中提供了一個WaitHandle 的抽象基類,此類就是包裝了一個Windows內核對象的句柄(句柄可以理解為標示了對象實例的一個數字),在.net Framework中提供了從WaitHandle類中派生的類。繼承關系如下所示:
WaitHandle
EventWaitHandle
AutoResetEvent
ManualResetEvent
Semaphore
Mutex
當我們在使用 AutoResetEvent,ManualResetEvent,Semaphore,Mutex這些類的時候,用構造函數來實例化這些類的對象時,其內部都調用了Win32 CreateEvent或CreateEvent函數,或CreateSemaphore或者CreateMutex函數,這些函數調用返回的句柄值都保存在WaitHandle基類定義的SafeWaitHandle字段中。
二、AutoResetEvent (自動重置事件)
AutoResetEvent 在獲得信號后,會自動將事件設置為無信號狀態。
例1:事件初始化為無信號狀態,主線程等待一段時間將事件設置為有信號狀態

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 8 namespace ThreadEvent 9 { 10 class Program 11 { 12 public static AutoResetEvent autoEvent = new AutoResetEvent(false); 13 static void Main(string[] args) 14 { 15 Task task = new Task(ThreadFunc); 16 task.Start(); 17 Console.WriteLine($"{DateTime.Now} Printed in main"); 18 Thread.Sleep(5000); 19 Console.WriteLine($"{DateTime.Now} Set signal in main"); 20 autoEvent.Set(); 21 Console.Read(); 22 } 23 private static void ThreadFunc() 24 { 25 PrintThreadInfo($"Printed in thread func"); 26 } 27 private static void PrintThreadInfo(string info) 28 { 29 if (autoEvent.WaitOne()) 30 { 31 //autoEvent.WaitOne(); 32 Console.WriteLine($"{DateTime.Now} {info}"); 33 Console.WriteLine($"{DateTime.Now} ThreadId:{Thread.CurrentThread.ManagedThreadId}\nIsBackgroundThread:{Thread.CurrentThread.IsBackground}\nIsThreadPoolThread:{Thread.CurrentThread.IsThreadPoolThread}"); 34 } 35 36 } 37 } 38 }
運行結果如下:
例2:事件初始化為無信號狀態,主線程等待一段時間將事件設置為有信號狀態,子線程連續兩次Wait,觀察第二次Wait的結果

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 8 namespace ThreadEvent 9 { 10 class Program 11 { 12 public static AutoResetEvent autoEvent = new AutoResetEvent(false); 13 static void Main(string[] args) 14 { 15 Task task = new Task(ThreadFunc); 16 task.Start(); 17 Console.WriteLine($"{DateTime.Now} Printed in main"); 18 Thread.Sleep(5000); 19 Console.WriteLine($"{DateTime.Now} Set signal in main"); 20 autoEvent.Set(); 21 Console.Read(); 22 } 23 private static void ThreadFunc() 24 { 25 PrintThreadInfo($"Printed in thread func"); 26 } 27 private static void PrintThreadInfo(string info) 28 { 29 if (autoEvent.WaitOne()) 30 { 31 if (autoEvent.WaitOne(4000)) 32 { 33 Console.WriteLine($"{DateTime.Now} {info}"); 34 Console.WriteLine($"{DateTime.Now} ThreadId:{Thread.CurrentThread.ManagedThreadId}\nIsBackgroundThread:{Thread.CurrentThread.IsBackground}\nIsThreadPoolThread:{Thread.CurrentThread.IsThreadPoolThread}"); 35 } 36 else 37 { 38 Console.ForegroundColor = ConsoleColor.Red; 39 Console.WriteLine($"{DateTime.Now} WaitOne timeout!"); 40 Console.ResetColor(); 41 } 42 } 43 44 } 45 } 46 }
運行結果如下:
三、ManualResetEvent(手動重置事件)
ManualResetEvent在獲得信號后,會一直保持有信號狀態,除非我們手動調用Reset來將事件設置為無信號狀態。
例1:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 8 namespace ThreadEvent 9 { 10 class Program 11 { 12 public static ManualResetEvent autoEvent = new ManualResetEvent(false); 13 static void Main(string[] args) 14 { 15 Task task = new Task(ThreadFunc); 16 task.Start(); 17 Console.WriteLine($"{DateTime.Now} Printed in main"); 18 Thread.Sleep(5000); 19 Console.WriteLine($"{DateTime.Now} Set signal in main"); 20 autoEvent.Set(); 21 Console.Read(); 22 } 23 private static void ThreadFunc() 24 { 25 PrintThreadInfo($"Printed in thread func"); 26 } 27 private static void PrintThreadInfo(string info) 28 { 29 if (autoEvent.WaitOne()) 30 { 31 //autoEvent.Reset(); 32 if (autoEvent.WaitOne(4000)) 33 { 34 Console.WriteLine($"{DateTime.Now} {info}"); 35 Console.WriteLine($"{DateTime.Now} ThreadId:{Thread.CurrentThread.ManagedThreadId}\nIsBackgroundThread:{Thread.CurrentThread.IsBackground}\nIsThreadPoolThread:{Thread.CurrentThread.IsThreadPoolThread}"); 36 } 37 else 38 { 39 Console.ForegroundColor = ConsoleColor.Red; 40 Console.WriteLine($"{DateTime.Now} WaitOne timeout!"); 41 Console.ResetColor(); 42 } 43 } 44 45 } 46 } 47 }
運行結果如下: