. NET synchronous and asynchronous EventWaitHandle (Event Notification) (13), eventwaithandle
Next:. NET synchronization and asynchronous Mutex (12)
In the previous article, we have mentioned that Mutex and the protagonists in this article inherit from WaitHandle directly or indirectly:
- Mutex class, which we have discussed in the previous article.
- EventWaitHandle class and its derived classes AutoResetEvent and ManualResetEvent.
- Semaphore class, that is, semaphores. Let's talk about it later (I suddenly don't think it is necessary to introduce it ).
WaitHandle provides several methods for synchronization. A WaitOne () has been mentioned in the previous blog about Mutex, which is an instance method. In addition, WaitHandle has three static methods for synchronization:
- SignalAndWait (WaitHandle, WaitHandle): sends a signal to the first WaitHandle in the form of an atomic operation and waits for the second. That is, wake up the thread/process blocking on the first WaitHandle, and wait for the second WaitHandle, and these two actions are atomic. Like WaitOne (), this method has two additional overload methods, Int32 or TimeSpan, respectively, to define the wait timeout time and whether to exit from the context synchronization domain.
- WaitAll (WaitHandle []): This is used to wait for all the Members in the WaitHandle array. This method is a good choice if you need to wait for all of the previous tasks to continue. There are still two overload methods used to control wait timeout, please refer.
- WaitAny (WaitHandle []): Unlike WaitAll (), WaitAny returns only when a member in the array receives a signal. For a job, you only need to wait for the fastest completion to start, then WaitAny () is what you need. It also has two reloads used to control the wait timeout.
Thread correlation
- Mutex is thread-related like Monitor. As we have mentioned before, only by using Monitor. enter ()/TryEnter () the thread that obtains the object lock can call Pulse ()/Wait ()/Exit (); similarly, only the thread that obtains the Mutex ownership can execute ReleaseMutex () method. Otherwise, an exception is thrown. This is the so-called thread correlation.
- Conversely, EventWaitHandle and Its Derived classes AutoResetEvent and ManualResetEvent are all thread-independent. Any thread can send a signal to EventWaitHandle to wake up and block the above thread.
- The Semaphore mentioned in the next article is thread-independent.
Event Notification
EventWaitHandle, AutoResetEvent, and ManualResetEvent have an "Event" in their names. However, this has nothing to do with the Event mechanism of. net. It does not involve any delegate or Event handler. Compared with the previous monitoring and Mutex tasks that require threads to compete for "locks", we can regard them as "events" that require threads to wait ". The thread blocks itself by waiting for the occurrence of these events. Once the "event" is completed, the blocked thread can continue to work after receiving the signal.
To work with the three static methods SingnalAndWait ()/WailAny ()/WaitAll () on WaitHandle, EventWaitHandle provides its own unique method for "Event" to complete and start again:
- Bool: Set (): English version of MSDN: Sets the state of the event to signaled, allowing one or more waiting threads to proceed; Chinese version of MSDN: Set the event state to the terminated state, one or more waiting threads are allowed to continue. It seems that "signaled" and "termination" do not correspond to each other at the beginning, but there is no conflict between the two. If the event is in progress, of course there is no "termination", then other threads need to wait. Once the event is completed, the event will be "terminated, so we send a signal to wake up the waiting thread, so the "signal sent" status is also reasonable. Two small details:
- Bool: Reset (): Sets the state of the event to nonsignaled, causing threads to block. Sets the event status to non-terminating, leading to thread blocking. Similarly, we need to understand that "nonsignaled" and "non-termination" are the same thing. Similarly, there is still a value with no response header. The Reset () function is equivalent to placing the event in progress again. Then all WaitOne ()/WaitAll ()/WaitAny ()/SignalAndWait () the thread of this event will be blocked again.
Constructor
Let's take a look at one of the simplest EventWaitHandle constructors:
- EventWaitHandle (Boolean initialState, EventResetMode mode): initializes a new instance of the EventWaitHandle class, specifies whether the waiting handle is in the terminated State, and whether it is automatically reset or manually reset. In most cases, we use false in the first parameter, so that the new instance is in the "non-terminated" status by default. The second parameter EventResetMode is an enumeration with two values:
Now we can clearly know when Set () is similar to Monitor. Pulse ()/PulseAll:
- When EventWaitHandle works in AutoReset mode, Set () is similar to Monitor. Pulse () in terms of wake-up function. At this time, Set () can only wake up one of the many (if there are multiple) blocked threads. However, there are still some differences between the two:
- When the Set () method of EventWaitHandle that works in ManualReset mode is called, it will wake up and Monitor. similar to PulseAll (), all blocked threads will receive a signal to wake up. The difference between the two is exactly the same as above.
Let's take a look at other constructors of EventWaitHandle:
- EventWaitHandle (Boolean initialState, EventResetMode mode, String name): we have seen the first two parameters. The third parameter name is used to specify the name of the synchronization event within the system. Yes, as we mentioned in Mutex, because the parent WaitHandle class has the ability to cross-process domains, like Mutex, we can create a global EventWaitHandle, use it for inter-process notifications. Note that the name is still case sensitive and there are still issues with the naming prefix. For details, refer to here. When name is null or a null String, this is equivalent to creating a local unnamed EventWaitHandle. Still, because EventWaitHandle with the same name already exists in the system, only one instance is returned to indicate EventWaitHandle with the same name. So in the end, if you still need to know whether the EventWaitHandle was created first by you, you need to use one of the following two constructors.
- EventWaitHandle (Boolean initialState, EventResetMode mode, String name, out Boolean createdNew): createdNew indicates whether EventWaitHandle is successfully created. true indicates success. false indicates that an event with the same name already exists.
- EventWaitHandle (Boolean initialState, EventResetMode mode, String name, out Boolean createdNew, EventWaitHandleSecurity): For security issues, check the example on this constructor. Global MutexEventWaitHandle security issues should be more important than Mutex, because it is possible that hackers use the same event name to send signals or organize your threads, this may seriously harm your business logic.
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 ();}}Demo
Appendix, Demo: http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
See more: Casual guide: synchronous and asynchronous
(This series is coming soon)