使用AutoResetEvent,ManualResetEvent和ManualResetEventSlim类进行线程间通信
AutoResetEvent和ManualResetEvent允许线程通过发信号进行通信。
两者都有两个信号量:True和False。都通过Set()和ReSet()来设置。并且使用WaitOne()的方法阻止当前的线程。
不同的是AutoResetEvent在调用Set()把信号量释放后(信号量设置为True),然后自动返回到终止状态(信号量为False).
线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。 如果 AutoResetEvent 为非终止状态,则线程会被阻止,并等待当前控制资源的线程通过调用 Set 来通知资源可用。
调用 Set 向 AutoResetEvent 发信号以释放等待线程。 AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。 如果没有任何线程在等待,则状态将无限期地保持为终止状态。
如果当 AutoResetEvent 为终止状态时线程调用 WaitOne,则线程不会被阻止。 AutoResetEvent 将立即释放线程并返回到未触发状态。
ManualResetEvent在调用Set()后,需要手动调用ReSet()恢复到终止状态。
ManualResetEventSlim是ManualResetEvent的简化版本,是4.0新增的类。使用Wait()方法来阻止线程。
MSDN上的代码很好的解释了如何使用:
Code:
1 class Example 2 { 3 private static AutoResetEvent event_1 = new AutoResetEvent(true); 4 private static AutoResetEvent event_2 = new AutoResetEvent(false); 5 6 static void Main() 7 { 8 Console.WriteLine("Press Enter to create three threads and start them.\r\n" + 9 "The threads wait on AutoResetEvent #1, which was created\r\n" + 10 "in the signaled state, so the first thread is released.\r\n" + 11 "This puts AutoResetEvent #1 into the unsignaled state."); 12 Console.ReadLine(); 13 14 for (int i = 1; i < 4; i++) 15 { 16 Thread t = new Thread(ThreadProc); 17 t.Name = "Thread_" + i; 18 t.Start(); 19 } 20 Thread.Sleep(1000); 21 22 for (int i = 0; i < 2; i++) 23 { 24 Console.WriteLine("Press Enter to release another thread."); 25 Console.ReadLine(); 26 event_1.Set(); 27 Thread.Sleep(250); 28 } 29 30 Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2."); 31 for (int i = 0; i < 3; i++) 32 { 33 Console.WriteLine("Press Enter to release a thread."); 34 Console.ReadLine(); 35 event_2.Set(); 36 Thread.Sleep(250); 37 } 38 39 // Visual Studio: Uncomment the following line. 40 //Console.Readline(); 41 } 42 43 static void ThreadProc() 44 { 45 string name = Thread.CurrentThread.Name; 46 47 Console.WriteLine("{0} waits on AutoResetEvent #1.", name); 48 event_1.WaitOne(); 49 Console.WriteLine("{0} is released from AutoResetEvent #1.", name); 50 51 Console.WriteLine("{0} waits on AutoResetEvent #2.", name); 52 event_2.WaitOne(); 53 Console.WriteLine("{0} is released from AutoResetEvent #2.", name); 54 55 Console.WriteLine("{0} ends.", name); 56 } 57 }
1 public class Example 2 { 3 private static ManualResetEvent mre = new ManualResetEvent(false); 4 5 static void Main() 6 { 7 Console.WriteLine("\nStart 3 named threads that block on a ManualResetEvent:\n"); 8 9 for (int i = 0; i <= 2; i++) 10 { 11 Thread t = new Thread(ThreadProc); 12 t.Name = "Thread_" + i; 13 t.Start(); 14 } 15 16 Thread.Sleep(500); 17 Console.WriteLine("\nWhen all three threads have started, press Enter to call Set()" + 18 "\nto release all the threads.\n"); 19 Console.ReadLine(); 20 21 mre.Set(); 22 23 Thread.Sleep(500); 24 Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" + 25 "\ndo not block. Press Enter to show this.\n"); 26 Console.ReadLine(); 27 28 for (int i = 3; i <= 4; i++) 29 { 30 Thread t = new Thread(ThreadProc); 31 t.Name = "Thread_" + i; 32 t.Start(); 33 } 34 35 Thread.Sleep(500); 36 Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" + 37 "\nwhen they call WaitOne().\n"); 38 Console.ReadLine(); 39 40 mre.Reset(); 41 42 // Start a thread that waits on the ManualResetEvent. 43 Thread t5 = new Thread(ThreadProc); 44 t5.Name = "Thread_5"; 45 t5.Start(); 46 47 Thread.Sleep(500); 48 Console.WriteLine("\nPress Enter to call Set() and conclude the demo."); 49 Console.ReadLine(); 50 51 mre.Set(); 52 53 // If you run this example in Visual Studio, uncomment the following line: 54 //Console.ReadLine(); 55 } 56 57 private static void ThreadProc() 58 { 59 string name = Thread.CurrentThread.Name; 60 61 Console.WriteLine(name + " starts and calls mre.WaitOne()"); 62 63 mre.WaitOne(); 64 65 Console.WriteLine(name + " ends."); 66 } 67 }
1 class MRESDemo 2 { 3 static void Main() 4 { 5 MRES_SetWaitReset(); 6 MRES_SpinCountWaitHandle(); 7 Console.ReadKey(); 8 } 9 static void MRES_SetWaitReset() 10 { 11 ManualResetEventSlim mres1 = new ManualResetEventSlim(false); // initialize as unsignaled 12 ManualResetEventSlim mres2 = new ManualResetEventSlim(false); // initialize as unsignaled 13 ManualResetEventSlim mres3 = new ManualResetEventSlim(true); // initialize as signaled 14 15 var observer = Task.Factory.StartNew(() => 16 { 17 mres1.Wait(); 18 Console.WriteLine("observer sees signaled mres1!"); 19 Console.WriteLine("observer resetting mres3..."); 20 mres3.Reset(); 21 Console.WriteLine("observer signalling mres2"); 22 mres2.Set(); 23 }); 24 Console.WriteLine("main thread: mres3.IsSet = {0} (should be true)", mres3.IsSet); 25 Console.WriteLine("main thread signalling mres1"); 26 mres1.Set(); 27 mres2.Wait(); 28 29 Console.WriteLine("main thread sees signaled mres2!"); 30 Console.WriteLine("main thread: mres3.IsSet = {0} (should be false)", mres3.IsSet); 31 32 observer.Wait(); // make sure that this has fully completed 33 mres1.Dispose(); 34 mres2.Dispose(); 35 mres3.Dispose(); 36 } 37 38 static void MRES_SpinCountWaitHandle() 39 { 40 // Construct a ManualResetEventSlim with a SpinCount of 1000 41 // Higher spincount => longer time the MRES will spin-wait before taking lock 42 ManualResetEventSlim mres1 = new ManualResetEventSlim(false, 1000); 43 ManualResetEventSlim mres2 = new ManualResetEventSlim(false, 1000); 44 45 Task bgTask = Task.Factory.StartNew(() => 46 { 47 Thread.Sleep(100); 48 Console.WriteLine("Task signalling both MRESes"); 49 mres1.Set(); 50 mres2.Set(); 51 }); 52 53 WaitHandle.WaitAll(new WaitHandle[] { mres1.WaitHandle, mres2.WaitHandle }); 54 Console.WriteLine("WaitHandle.WaitAll(mres1.WaitHandle, mres2.WaitHandle) completed."); 55 56 bgTask.Wait(); 57 mres1.Dispose(); 58 mres2.Dispose(); 59 } 60 }