AutoResetEvent,继承自EventWaitHandle,用在多线程中保护对共享资源的访问,保证每次只能有一个线程对共享资源进行访问。
AutoResetEvent最特别之处,在于每次保证只有一个线程执行逻辑,其他的线程只能等待。
AutoResetEvent通过信号量来阻塞和释放线程。其基本原理如下:
1、如果AutoResetEvent在无信号状态时,执行了WaitOne()方法之后,当前线程就会被阻塞,直到该AutoResetEvent在其他地方执行了Set()方法后,此时AutoResetEvent就获得了信号并释放一个线程,然后自动执行Reset()方法(即失去信号)。
如果当AutoResetEvent执行Set()方法后,没有等待任何等待的线程,则该AutoResetEvent会一直保持持有信号;
2、如果AutoResetEvent在有信号时,执行了WaitOne方法,当前线程会被立即释放,并且AutoResetEvent也会失去信号。
代码如下所示:
static AutoResetEvent autoResetEvent=new AutoResetEvent(true);
static void WorkOnAutoResetEvent(string threadName) { Console.WriteLine($"{threadName} 正在等待中..."); autoResetEvent.WaitOne(); Console.WriteLine($"{threadName} 执行中..."); Thread.Sleep(1000); Console.WriteLine($"{threadName} 执行完毕!"); //得到信号,并释放一个被阻塞的线程,然后丢失信号(即自动执行Reset()方法);如果没有线程可以被释放,则它会一直保持信号 autoResetEvent.Set(); }
Main中代码如下所示:
for (int i = 0; i < 3; i++) { string threadName = $"thread_{i}"; var thread = new Thread(delegate () { WorkOnAutoResetEvent(threadName); }); thread.Start(); }
结果:
为何AutoResetEvent会被以Auto开始的单词命名,因为它在得到信号并释放一个被阻塞的线程之后,会自动的执行Reset()方法,即自动丢失信号,这就是它的自动之处。
当然,如果它在得到信号,但此时并没有任何阻塞的线程被释放,它就会一直保持持有信号,直到下一个WaitOne()方法被执行。
即在调用Set()方法之后,它就获得了信号,如果释放了一个阻塞的线程,它会自动执行Reset()丢失信号;如果没有释放线程,则它不会丢失信号。这是关键之处!
参考文献:https://docs.microsoft.com/en-us/dotnet/api/system.threading.autoresetevent?view=netcore-3.1#explicit-interface-implementations