Home  >  Article  >  Backend Development  >  .NET Synchronization and Asynchronous EventWaitHandle

.NET Synchronization and Asynchronous EventWaitHandle

大家讲道理
大家讲道理Original
2017-04-11 14:05:402779browse


In the previous article we have mentioned Mutex and the protagonists of this article directly or indirectlyInherits from WaitHandle:

  • Mutex class, which we have already talked about in the previous article.

  • EventWaitHandle class and its derived classes AutoResetEvent and ManualResetEvent, this is the protagonist of this article. ##map

    hore class, that is, semaphore, we will talk about it in the next article (suddenly I feel there is no need to introduce it)
  • WaitHandle provides several. Methods for synchronization. The previous blog about Mutex has already mentioned a WaitOne(), which is an instance method. In addition, WaitHandle has three other
  • static
methods. :

##SignalAndWait(WaitHandle, WaitHandle): In the form of atomic operation, send a signal to the first WaitHandle and wait for the second one, that is, wake up the thread blocked on the first WaitHandle. / process, and then wait for the second WaitHandle, and these two actions are atomic. Like WaitOne(), this method also has two overloaded methods, using Int32 or

respectively. Time
    Span to define the wait timeout and whether to
  • exit

    from the synchronization domain of the contextWaitAll(WaitHandle[. ]): This is used to wait for all members in the WaitHandlearray. If a job needs to wait for everyone before it to complete, then this method is still a good choice. For overloaded methods that control waiting timeout, please refer to

  • WaitAny(WaitHandle[]): Unlike WaitAll(), WaitAny only waits until one member of the array receives a signal. Return. If you only need to wait for the fastest completion of a job, then WaitAny() is what you need. It also has two overloads for controlling wait timeout ##.

  • #Thread dependency
  • ##Mutex, like Monitor, has thread dependency. We have mentioned it before. Previously, only threads that obtained the
object

lock through Monitor.Enter()/TryEnter() can call Pulse()/Wait()/Exit(); similarly, only threads that obtain Mutex ownership can Execute the ReleaseMutex() method, otherwise an exception will be thrown. This is called thread dependency.

In contrast, EventWaitHandle and its derived classes AutoResetEvent and ManualResetEvent are thread-independent. Any thread can signal to EventWaitHandle to wake up the thread blocked on it.

  • The Semaphore to be mentioned in the next article is also thread-independent.

  • Event notification
  • EventWaitHandle, AutoResetEvent, and ManualResetEvent all have an "Event" in their names, but this is not the same as. net's own

    event
  • mechanism has nothing to do with it, it does not involve any delegation or
event handling

procedures. Compared with the Monitor and Mutex we encountered before, which require threads to compete for "locks", we can understand them as some "events" that require threads to wait. The thread blocks itself by waiting for these events to "occur". Once the "event" is completed, the blocked thread can continue working after receiving the signal.

In order to cooperate with the three static methods SingnalAndWait()/WailAny()/WaitAll() on WaitHandle, EventWaitHandle provides its own unique method to complete and restart "Event":

bool:Set(): English version MSDN: Sets the state of the event to signaled, allowing one or more waiting threads to proceed; Chinese version MSDN: Sets the event stateSet to terminated state, allowing one or more waiting threads to continue. At first glance, "signaled" and "termination" don't seem to correspond, but when you think about it carefully, the two terms are actually not contradictory. If the event is in progress, of course there is no "termination", then other

threads need to wait; once the event is completed, then the event is "termination", so we send a signal to wake up the waiting thread, so the "signal Sent" status is also reasonable. Two small details:

  1. No matter the Chinese or English version, it is mentioned that this method can make "one" or "multiple" waiting threads "continue/Proceed" (note that it is not "wake up"). So this method is similar to Monitor.Pulse() and Monitor.PulseAll() in the "wake up" action. As for when it is similar to Pulse() and when it is similar to PulseAll(), read on.

  2. This method has a bool return value: true if the operation is successful; otherwise, false. However, MSDN does not tell us when the execution will fail. You can only ask a Microsoft MVP.

  • ##bool:Reset(): Sets the state of the event to nonsignaled, causing threads to block. Sets the state of the event to nonsignaled, causing threads to block. Likewise, we need to understand that "nonsignaled" and "non-terminated" are the same thing. Also, there is still a nonsensical return value. The function of Reset() is equivalent to making the event "in progress" again, then all threads of the WaitOne()/WaitAll()/WaitAny()/SignalAndWait() event will be blocked again.

  • Constructor

    Let’s take a look at the most common constructor of EventWaitHandle Simple one:

    • EventWaitHandle(Boolean initialState, EventResetMode mode): Initializes a new instance of the EventWaitHandle class and specifies whether the wait handle is initially in a terminated state, and whether it is reset automatically or manually Reset. Most of the time we will use false in the first parameter so that the new instance will default to the "non-terminated" state. The second parameter EventResetMode is an enumeration with a total of two values:

    1. EventResetMode.AutoReset: When Set() is called, the current EventWaitHandle is transferred to In the terminated state, if a thread is blocked on the current EventWaitHandle, the EventWaitHandle will automatically reset (equivalent to automatically calling Reset()) after releasing a thread and transfer to the non-terminated state again, and the remaining original Blocked threads (if any) will continue to block. If no thread is blocked after calling Set(), then the EventWaitHandle will remain in the "terminated" state until a thread tries to wait for the event. This thread will not be blocked. After that, the EventWaitHandle will automatically reset and block all threads after that.

    2. ##EventResetMode.ManualReset

      : When terminated, EventWaitHandle releases all waiting threads and before manual reset, that is, Reset() It remains terminated until called.

    Okay, now we can clearly know when Set() is similar to Monitor.Pulse()/PulseAll() respectively:

      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 many (if there are multiple) blocked threads. But there are still some differences between the two:
      The function of Set() is not just to "wake up" but to "release", allowing the thread to continue working (proceed); on the contrary, The thread awakened by Pulse() only re-enters the Running state and participates in the competition for the object lock. No one can guarantee that it will obtain the object lock.
    1. The called state of Pulse() will not be maintained. Therefore, if Pulse() is called when there are no waiting threads, the next thread that calls Monitor.Wait() will still be blocked, as if Pulse() had never been called. In other words, Monitor.Pulse() only takes effect when it is called, unlike Set(), which will continue to the next WaitXXX().
    When the Set() method of an EventWaitHandle working in ManualReset mode is called, its wake-up function is similar to Monitor.PulseAll(). All are Blocked threads will receive the signal and be awakened. 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 already seen the first two parameters, and the third parameter name is used to specify synchronization events within the system. The name. Yes, as we mentioned in the Mutex article, since the parent class WaitHandle has the ability to cross process domains, like Mutex, we can create a global EventWaitHandle and later use it for inter-process notifications . Note that name is still case-sensitive, and there are still naming prefix issues, you can refer to here. When name is null or an empty string, this is equivalent to creating a local, unnamed EventWaitHandle. Still the same, it is possible that only one instance is returned to represent the EventWaitHandle with the same name because there is already an EventWaitHandle with the same name in the system. So in the end it's still the same, if you need to know whether this EventWaitHandle was created by you first, you need to use one of the following two constructors.

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out Boolean createdNew): createdNew is used to indicate whether the EventWaitHandle was 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): Regarding security issues, just check the example on this constructor . The security issues of global MutexEventWaitHandle should be paid more attention to than that of Mutex, because it is possible for hackers to use the same event name to send signals or organize your threads, which 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();
        }
    }


    The above is the detailed content of .NET Synchronization and Asynchronous EventWaitHandle. For more information, please follow other related articles on the PHP Chinese website!

    Statement:
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn