从 .NET Framework 版本2.0 开始, AutoResetEvent 和ManualResetEvent 派生自 EventWaitHandle 类。 在 ManualResetEvent 功能上等效于 EventWaitHandle 使用创建的EventResetMode.ManualReset。使用 EventResetMode.AutoReset 标志创建的 EventWaitHandle 会在释放单个等待线程后自动进行重置。以下主要介绍.NET(C#)中AutoResetEvent 和 ManualResetEvent的使用。
ManualResetEvent表示线程同步事件,收到信号时,必须手动重置该事件。AutoResetEvent表示线程同步事件在一个等待线程释放后收到信号时自动重置。ManualResetEvent 调用一次Set()后将允许恢复所有被阻塞线程。需手动在调用WaitOne()之后调用Reset()重置信号量状态为非终止,然后再次调用WaitOne()时才能继续阻塞线程。AutoResetEvent调用一次Set()只能继续被阻塞的一个线程,多次调用Set()才行,但不需手动调用Reset();再次调用WaitOne()的时候又能阻塞线程。AutoResetEvent不保证每次调用 Set()方法都将释放一个线程。 如果两次调用都过于接近,因此在释放线程之前发生第二次调用,则只释放一个线程。 这就像第二次调用没有发生。 此外,如果在没有 Set()等待的线程并且已 AutoResetEvent 发出信号的情况下调用,则调用不起作用。
参考文档:AutoResetEvent 类 (System.Threading) | Microsoft Learn
AutoResetEvent(false)为创建在无信号状态。WaitOne()方法会阻塞,调用Set()方法变为有信号状态,只能继续被阻塞的一个线程的WaitOne()方法,会自动变为信号状态,不需手动调用Reset()。new AutoResetEvent(true)为创建在有信号状态。调用Set()方法变为有信号状态,只能继续被阻塞的一个线程的WaitOne()方法,会自动变为信号状态,不需手动调用Reset()。
例如,
- using System;
- using System.Threading;
- namespace ConsoleApplication
- {
- class Program
- {
- private static AutoResetEvent event_1 = new AutoResetEvent(true);
- private static AutoResetEvent event_2 = new AutoResetEvent(false);
- static void Main()
- {
- for (int i = 1; i < 4; i++)
- {
- Thread t = new Thread(ThreadProc);
- t.Name = "Thread_" + i;
- t.Start();
- }
- Thread.Sleep(250);
- for (int i = 0; i < 2; i++)
- {
- Console.WriteLine("按Enter键释放另一个线程。");
- Console.ReadLine();
- event_1.Set();
- Thread.Sleep(250);
- }
- Console.WriteLine("\r\nevent_2所有线程现在都在等待");
- for (int i = 0; i < 3; i++)
- {
- Console.WriteLine("按Enter键释放线程。");
- Console.ReadLine();
- event_2.Set();
- Thread.Sleep(250);
- }
- }
- static void ThreadProc()
- {
- string name = Thread.CurrentThread.Name;
- Console.WriteLine("{0} 等待event_1", name);
- event_1.WaitOne();
- Console.WriteLine("{0} 从event_1释放", name);
- Console.WriteLine("{0} 等待event_2", name);
- event_2.WaitOne();
- Console.WriteLine("{0} 从event_2释放", name);
- Console.WriteLine("{0} 结束", name);
- }
- }
- }
参考文档:ManualResetEvent 类 (System.Threading) | Microsoft Learn
new ManualResetEvent(false)为创建在无信号状态。WaitOne()方法会阻塞,调用Set()方法变为有信号状态,WaitOne()方法不会阻塞,需要调用Reset()方法变为无信号状态,WaitOne()方法将会阻塞。new ManualResetEvent(true)为创建在有信号状态。Wait()方法不会阻塞,需要调用Reset()方法变为无信号状态,Wait()方法将会阻塞。
例如,
- using System;
- using System.Threading;
- namespace ConsoleApplication
- {
- class Program
- {
- mre用于手动阻塞和释放线程。false是创建在无信号状态。
- private static ManualResetEvent mre = new ManualResetEvent(false);
- static void Main()
- {
- Console.WriteLine("启动 3个在ManualResetEvent上阻塞的命名线程");
- for (int i = 0; i <= 2; i++)
- {
- Thread t = new Thread(ThreadProc);
- t.Name = "Thread_" + i;
- t.Start();
- }
- Thread.Sleep(500);
- Console.WriteLine("按回车键,当三个线程都启动后,调用Set()释放所有线程。");
- Console.ReadLine();
- mre.Set();
- Thread.Sleep(500);
- Console.WriteLine("按回车键,当ManualResetEvent有信号时,调用WaitOne()的线程不会阻塞。");
- Console.ReadLine();
- for (int i = 3; i <= 4; i++)
- {
- Thread t = new Thread(ThreadProc);
- t.Name = "Thread_" + i;
- t.Start();
- }
- Thread.Sleep(500);
- Console.WriteLine("按回车键,调用Reset(),这样线程在调用WaitOne()时就会再次阻塞。");
- Console.ReadLine();
- mre.Reset();
- // Start a thread that waits on the ManualResetEvent.
- Thread t5 = new Thread(ThreadProc);
- t5.Name = "Thread_5";
- t5.Start();
- Thread.Sleep(500);
- Console.WriteLine("按回车键,调用Set()并结束运行。");
- Console.ReadLine();
- mre.Set();
- // 如在Visual Studio中运行这个例子,取消下面这行注释:
- //Console.ReadLine();
- }
- private static void ThreadProc()
- {
- string name = Thread.CurrentThread.Name;
- Console.WriteLine(name + " 调用 mre.WaitOne()");
- mre.WaitOne();
- Console.WriteLine(name + " 结束");
- }
- }
- }