Mutex类,这个我们在上一篇已讲过。
EventWaitHandle 类及其派生类AutoResetEvent 和 ManualResetEvent,这是本篇的主角。
Semaphore 类,即信号量,我们下一篇再讲(遽然以为没有必要引见了)。
WaitHandle供应了多少用于同步的要领。上一篇关于Mutex的blog中已讲到一个WaitOne(),这是一个实例要领。除此之外,WaitHandle尚有3个用于同步的静态要领:
SignalAndWait(WaitHandle, WaitHandle):以原子操纵的情势,向第一个WaitHandle发出信号并守候第二个。即叫醒壅塞在第一个WaitHandle上的线程/历程,然后自身守候第二个WaitHandle,且这两个行动是原子性的。跟WaitOne()一样,这个要领尚有两个重载要领,离别用Int32或许TimeSpan来定义守候超时时刻,以及是不是从上下文的同步域中退出。
WaitAll(WaitHandle[]):这是用于守候WaitHandle数组里的一切成员。假如一项事变,须要守候前面一切人完成才继承,那末这个要领就是一个很好的挑选。依然有两个用于掌握守候超时的重载要领,请自行参阅。
WaitAny(WaitHandle[]):与WaitAll()差异,WaitAny只需比及数组中一个成员收到信号就会返回。假如一项事变,你只需等最快做完的谁人完成就能够最早,那末WaitAny()就是你所须要的。它一样有两个用于掌握守候超时的重载。
线程相干性
Mutex与Monitor一样,是具有线程相干性的。我们之前已提到过,只要经由过程Monitor.Enter()/TryEnter()取得对象锁的线程才挪用Pulse()/Wait()/Exit();一样的,只要取得Mutex拥有权的线程才实行ReleaseMutex()要领,不然就会激发非常。这就是所谓的线程相干性。
相反,EventWaitHandle以及它的派生类AutoResetEvent和ManualResetEvent都是线程无关的。任何线程都能够发信号给EventWaitHandle,以叫醒壅塞在上面的线程。
下一篇要提到的Semaphore也是线程无关的。
Event关照
EventWaitHandle、AutoResetEvent、ManualResetEvent名字里都有一个“Event”,不过这跟.net的自身的事宜机制完整没有关系,它不触及任何托付或事宜处置惩罚顺序。相对于我们之前遇到的Monitor和Mutex须要线程去争取“锁”而言,我们能够把它们理解为一些须要线程守候的“事宜”。线程经由过程守候这些事宜的“发作”,把自身壅塞起来。一旦“事宜”完成,被壅塞的线程在收到信号后就能够继承事变。
为了合营WaitHandle上的3个静态要领SingnalAndWait()/WailAny()/WaitAll(),EventWaitHandle供应了自身独占的,使“Event”完成和从新最早的要领:
bool:Set():英文版MSDN:Sets the state of the event to signaled, allowing one or more waiting threads to proceed;中文版MSDN:将事宜状况设置为停止状况,许可一个或多个守候线程继承。初看“signaled”和“停止”好像并不对应,细想起来这二者的说法实在也不抵牾。事宜假如在举行中,固然就没有“停止”,那末别的线程就须要守候;一旦事宜完成,那末事宜就“停止”了,因而我们发送信号叫醒守候的线程,所以“信号已发送”状况也是合理的。两个小细节:
不管中文照样英文版,都提到这个要领都是能够让“一个”或“多个”守候线程“继承/Proceed”(注重不是“叫醒”)。所以这个要领在“叫醒”这个行动上是相似于Monitor.Pulse()和Monitor.PulseAll()的。至于什么时刻相似Pulse(),又在什么时刻相似PulseAll(),往下看。
这个要领有bool型的返回值:假如该操纵胜利,则为true;不然,为false。不过MSDN并没有通知我们,什么时刻实行会失利,你只要找个微软MVP问问了。
bool:Reset():Sets the state of the event to nonsignaled, causing threads to block. 将事宜状况设置为非停止状况,致使线程阻挠。 一样,我们须要邃晓“nonsignaled”和“非停止”是一回事变。还一样的是,依然有个无厘头的返回值。Reset()的作用,相当于让事宜从新最早处于“举行中”,那末今后一切WaitOne()/WaitAll()/WaitAny()/SignalAndWait()这个事宜的线程都邑再次被挡在门外。
构造函数
来看看EventWaitHandle浩瀚构造函数中最简朴的一个:
EventWaitHandle(Boolean initialState, EventResetMode mode):初始化EventWaitHandle类的新实例,并指定守候句柄最初是不是处于停止状况,以及它是自动重置照样手动重置。大多数时刻我们会在第一个参数里运用false,如许新实例会缺省为“非停止”状况。第二个参数EventResetMode是一个罗列,一共两个值:
EventResetMode.AutoReset:当Set()被挪用当前EventWaitHandle转入停止状况时,如有线程壅塞在当前EventWaitHandle上,那末在开释一个线程后EventWaitHandle就会自动重置(相当于自动挪用Reset())再次转入非停止状况,盈余的本来壅塞的线程(假如有的话)还会继承壅塞。假如挪用Set()后本没有线程壅塞,那末EventWaitHandle将坚持“停止”状况直到一个线程尝试守候该事宜,这个该线程不会被壅塞,今后EventWaitHandle才会自动重置并壅塞那以后的一切线程。
EventResetMode.ManualReset:当停止时,EventWaitHandle 开释一切守候的线程,并在手动重置前,即Reset()被挪用前,一向坚持停止状况。
好了,如今我们能够清晰的晓得Set()在什么时刻离别相似于Monitor.Pulse()/PulseAll()了:
当EventWaitHandle事变在AutoReset形式下,就叫醒功用而言,Set()与Monitor.Pulse()相似。此时,Set()只能叫醒浩瀚(假如有多个的话)被壅塞线程中的一个。但二者仍有些差异:
Set()的作用不仅仅是“叫醒”而是“开释”,能够让线程继承事变(proceed);相反,Pulse()叫醒的线程只是从新进入Running状况,介入对象锁的争取,谁都不能保证它一定会取得对象锁。
Pulse()的已被挪用的状况不会被保护。因而,假如在没有守候线程时挪用Pulse(),那末下一个挪用Monitor.Wait()的线程依然会被壅塞,就像Pulse() 没有被被挪用过。也就是说Monitor.Pulse()只在挪用当时发挥作用,并不象Set()的作用会延续到下一个WaitXXX()。
在一个事变在ManualReset形式下的EventWaitHandle的Set()要领被挪用时,它所起到的叫醒作用与Monitor.PulseAll()相似,一切被壅塞的线程都邑收到信号被叫醒。而二者的差异与上面完整雷同。
来看看EventWaitHandle的别的构造函数:
EventWaitHandle(Boolean initialState, EventResetMode mode, String name):头两个参数我们已看过,第三个参数name用于在体系范围内指定同步事宜的称号。是的,正如我们在Mutex一篇中提到的,由于父类WaitHandle是具有跨历程域的才能的,因而跟Mutex一样,我们能够建立一个全局的EventWaitHandle,让后将它用于历程间的关照。注重,name依然是大小写敏感的,依然有定名前缀的题目跟,你能够参照这里。当name为null或空字符串时,这等效于建立一个部分的未定名的EventWaitHandle。依然一样的另有,能够会由于已体系中已有同名的EventWaitHandle而仅仅返回一个实例示意同名的EventWaitHandle。所以末了依旧一样地,假如你须要晓得这个EventWaitHandle是不是由你最早建立,你须要运用以下两个构造函数之一。
EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out Boolean createdNew):createdNew用于表明是不是胜利建立了EventWaitHandle,true表明胜利,false表明已存在同名的事宜。
EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out Boolean createdNew, EventWaitHandleSecurity):关于平安的题目,直接检察这个构造函数上的例子吧。全局MutexEventWaitHandle的平安题目应当相对Mutex更须要注重,由于有能够黑客顺序用雷同的事宜名对你的线程发送信号或许举行构造,那样能够会严重危害你的营业逻辑。
MSDN Demo
using System;using System.Threading;public class Example { // The EventWaitHandle used to demonstrate the difference // between AutoReset and ManualReset synchronization events. // private static EventWaitHandle ewh; // A counter to make sure all threads are started and // blocked before any are released. A Long is used to show // the use of the 64-bit Interlocked methods. // private static long threadCount = 0; // An AutoReset event that allows the main thread to block // until an exiting thread has decremented the count. // private static EventWaitHandle clearCount = new EventWaitHandle(false, EventResetMode.AutoReset); [MTAThread] public static void Main() { // Create an AutoReset EventWaitHandle. // ewh = new EventWaitHandle(false, EventResetMode.AutoReset); // Create and start five numbered threads. Use the // ParameterizedThreadStart delegate, so the thread // number can be passed as an argument to the Start // method. for (int i = 0; i <= 4; i++) { Thread t = new Thread( new ParameterizedThreadStart(ThreadProc) ); t.Start(i); } // Wait until all the threads have started and blocked. // When multiple threads use a 64-bit value on a 32-bit // system, you must access the value through the // Interlocked class to guarantee thread safety. // while (Interlocked.Read(ref threadCount) < 5) { Thread.Sleep(500); } // Release one thread each time the user presses ENTER, // until all threads have been released. // while (Interlocked.Read(ref threadCount) > 0) { Console.WriteLine("Press ENTER to release a waiting thread."); Console.ReadLine(); // SignalAndWait signals the EventWaitHandle, which // releases exactly one thread before resetting, // because it was created with AutoReset mode. // SignalAndWait then blocks on clearCount, to // allow the signaled thread to decrement the count // before looping again. // WaitHandle.SignalAndWait(ewh, clearCount); } Console.WriteLine(); // Create a ManualReset EventWaitHandle. // ewh = new EventWaitHandle(false, EventResetMode.ManualReset); // Create and start five more numbered threads. // for(int i=0; i<=4; i++) { Thread t = new Thread( new ParameterizedThreadStart(ThreadProc) ); t.Start(i); } // Wait until all the threads have started and blocked. // while (Interlocked.Read(ref threadCount) < 5) { Thread.Sleep(500); } // Because the EventWaitHandle was created with // ManualReset mode, signaling it releases all the // waiting threads. // Console.WriteLine("Press ENTER to release the waiting threads."); Console.ReadLine(); ewh.Set(); } public static void ThreadProc(object data) { int index = (int) data; Console.WriteLine("Thread {0} blocks.", data); // Increment the count of blocked threads. Interlocked.Increment(ref threadCount); // Wait on the EventWaitHandle. ewh.WaitOne(); Console.WriteLine("Thread {0} exits.", data); // Decrement the count of blocked threads. Interlocked.Decrement(ref threadCount); // After signaling ewh, the main thread blocks on // clearCount until the signaled thread has // decremented the count. Signal it now. // clearCount.Set(); } }
以上就是.NET 同步与异步 之 EventWaitHandle的细致内容,更多请关注ki4网别的相干文章!