1、AutoResetEvent 和 ManualResetEvent
ManualResetEvent
表示线程同步事件,收到信号时,必须手动重置该事件。AutoResetEvent
表示线程同步事件在一个等待线程释放后收到信号时自动重置。ManualResetEvent
调用一次Set()后将允许恢复所有被阻塞线程。需手动在调用WaitOne()
之后调用Reset()
重置信号量状态为非终止,然后再次调用WaitOne()
时才能继续阻塞线程。AutoResetEvent
调用一次Set()
只能继续被阻塞的一个线程,多次调用Set()
才行,但不需手动调用Reset()
;再次调用WaitOne()
的时候又能阻塞线程。AutoResetEvent
不保证每次调用 Set()
方法都将释放一个线程。 如果两次调用都过于接近,因此在释放线程之前发生第二次调用,则只释放一个线程。 这就像第二次调用没有发生。 此外,如果在没有 Set()
等待的线程并且已 AutoResetEvent
发出信号的情况下调用,则调用不起作用。
2、AutoResetEvent的使用
参考文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.autoresetevent?view=net-6.0
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); } } }
3、ManualResetEvent的使用
参考文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.manualresetevent?view=net-6.0
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 + " 结束"); } } }