Heim  >  Artikel  >  Backend-Entwicklung  >  .NET-Synchronisierung und asynchrones EventWaitHandle

.NET-Synchronisierung und asynchrones EventWaitHandle

大家讲道理
大家讲道理Original
2017-04-11 14:05:402731Durchsuche


Im vorherigen Artikel haben wir Mutex und die Protagonisten dieses Artikels direkt oder indirekt Erbt von WaitHandle:

WaitHandle bietet mehrere Methoden zur Synchronisierung. Im vorherigen Blog über Mutex wurde bereits eine WaitOne() erwähnt, bei der es sich um eine Instanzmethode handelt. Darüber hinaus verfügt WaitHandle über drei weitere

statische Methoden. :

  • SignalAndWait(WaitHandle, WaitHandle): Senden Sie in Form einer atomaren Operation ein Signal an das erste WaitHandle und warten Sie auf das zweite, dh wecken Sie den blockierten Thread auf der erste WaitHandle. /-Prozess und dann auf den zweiten WaitHandle warten, und diese beiden Aktionen sind atomar, genau wie WaitOne(). Diese Methode verfügt auch über zwei

    überladene -Methoden, die Int32 bzw. verwenden. ZeitSpanne zum Definieren der Wartezeit und ob die Synchronisierungsdomäne des Kontexts verlassen werden soll

    Wait
  • All
  • (WaitHandle[ ]): Dies wird verwendet Um auf alle Mitglieder im WaitHandle

    -Array zu warten, ist diese Methode immer noch eine gute Wahl zu

    WaitAny(WaitHandle[]): Im Gegensatz zu WaitAll() wartet WaitAny nur, bis ein Mitglied des Arrays ein Signal empfängt eines Jobs, dann ist WaitAny() genau das, was Sie brauchen >Mutex weist wie Monitor eine Thread-Abhängigkeit auf. Zuvor haben nur Threads, die die Sperre
  • object
  • über Monitor.Enter()/TryEnter() erhalten haben, Pulse()/Wait() aufgerufen. /Exit(); In ähnlicher Weise können nur Threads, die Mutex-Besitz erhalten, die ReleaseMutex()-Methode ausführen, andernfalls wird eine Ausnahme ausgelöst. Dies wird als Thread-Abhängigkeit bezeichnet.

Im Gegensatz dazu sind EventWaitHandle und seine abgeleiteten Klassen AutoResetEvent und ManualResetEvent Thread-unabhängig. Jeder Thread kann ein Signal an EventWaitHandle senden, um den darauf blockierten Thread aufzuwecken.

Das im nächsten Artikel erwähnte Semaphor ist ebenfalls threadunabhängig.

  • Ereignisbenachrichtigung

  • EventWaitHandle, AutoResetEvent und ManualResetEvent haben alle ein „Event“ in ihren Namen, aber dieses ist das Gleiche wie Nets eigener

    Ereignis

    -Mechanismus. Er beinhaltet keine Delegaten oder
  • Ereignishandler
  • -Prozeduren. Im Vergleich zu Monitor und Mutex, auf die wir zuvor gestoßen sind und bei denen Threads um „Sperren“ konkurrieren müssen, können wir sie als einige „Ereignisse“ verstehen, bei denen Threads warten müssen. Der Thread blockiert sich selbst, indem er darauf wartet, dass diese Ereignisse „eintreten“. Sobald das „Ereignis“ abgeschlossen ist, kann der blockierte Thread nach Erhalt des Signals weiterarbeiten.

    Um mit den drei statischen Methoden SingnalAndWait()/WailAny()/WaitAll() auf WaitHandle zusammenzuarbeiten, stellt EventWaitHandle eine eigene einzigartige Methode zum Abschließen und Neustarten von „Event“ bereit:

bool:Set(): Englische Version MSDN: Setzt den Status des Ereignisses auf „signalisiert“, sodass ein oder mehrere wartende Threads fortfahren können. Chinesische Version: Setzt den Ereignis-Status Wird auf den beendeten Zustand gesetzt, sodass ein oder mehrere wartende Threads fortfahren können. Auf den ersten Blick scheinen „signalisiert“ und „Beendigung“ nicht übereinstimmend zu sein, aber wenn man genau darüber nachdenkt, sind die beiden Begriffe tatsächlich kein Widerspruch. Wenn das Ereignis läuft, gibt es natürlich keine „Beendigung“, dann müssen

andere

Threads warten; sobald das Ereignis abgeschlossen ist, ist das Ereignis „Beendigung“, also senden wir ein Signal zum Aufwecken den wartenden Thread hochzuladen, daher ist auch der Status „Signal“ „Gesendet“ sinnvoll. Zwei kleine Details:

  1. Unabhängig von der chinesischen oder englischen Version wird erwähnt, dass diese Methode „einen“ oder „mehrere“ wartende Threads zum „Fortfahren/Fortfahren“ veranlassen kann (beachten Sie, dass es sich nicht um „Wake“ handelt). hoch"). Diese Methode ähnelt also Monitor.Pulse() und Monitor.PulseAll() in der Aktion „Aufwachen“. Lesen Sie weiter, wann es Pulse() und wann PulseAll() ähnelt.

  2. Diese Methode hat einen Bool-Rückgabewert: true, wenn die Operation erfolgreich ist, andernfalls false. MSDN teilt uns jedoch nicht mit, wann die Ausführung fehlschlägt. Sie können nur einen Microsoft MVP fragen.

  • bool:Reset(): Setzt den Status des Ereignisses auf „Nicht signalisiert“, wodurch Threads blockiert werden. Setzt den Status des Ereignisses auf „Nicht signalisiert“, wodurch Threads blockiert werden. Ebenso müssen wir verstehen, dass „nicht signalisiert“ und „nicht terminiert“ dasselbe sind. Außerdem gibt es immer noch einen unsinnigen Rückgabewert. Die Funktion von Reset() ist äquivalent dazu, das Ereignis wieder auf „in Bearbeitung“ zu setzen, dann werden alle Threads des WaitOne()/WaitAll()/WaitAny()/SignalAndWait()-Ereignisses erneut blockiert.

  • Konstruktor

    Werfen wir einen Blick auf den gebräuchlichsten Konstruktor von EventWaitHandle Einfach:

    • EventWaitHandle(Boolean initialState, EventResetMode mode): Initialisiert eine neue Instanz der EventWaitHandle-Klasse und gibt an, ob sich das Wartehandle zunächst in einem beendeten Zustand befindet und ob dies der Fall ist Automatisch zurücksetzen oder manuell zurücksetzen. Meistens verwenden wir im ersten Parameter false, sodass die neue Instanz standardmäßig den Status „nicht beendet“ annimmt. Der zweite Parameter EventResetMode ist eine Enumeration mit insgesamt zwei Werten:

    1. EventResetMode.AutoReset: Beim Aufruf von Set() wird das aktuelle EventWaitHandle verwendet Übertragen auf Wenn im beendeten Zustand ein Thread auf dem aktuellen EventWaitHandle blockiert ist, wird EventWaitHandle automatisch zurückgesetzt (entspricht dem automatischen Aufruf von Reset()), nachdem ein Thread freigegeben und in den nicht beendeten Zustand übergegangen ist erneut, und die verbleibenden blockierten Threads (falls vorhanden) werden weiterhin blockiert. Wenn nach dem Aufruf von Set() kein Thread blockiert ist, bleibt EventWaitHandle im Status „beendet“, bis ein Thread versucht, auf das Ereignis zu warten. Danach wird EventWaitHandle automatisch zurückgesetzt und blockiert alle Threads danach.

    2. EventResetMode.ManualReset: Bei Beendigung gibt EventWaitHandle alle wartenden Threads vor dem manuellen Zurücksetzen frei, d. h. Reset() Es bleibt beendet, bis angerufen.

    OK, jetzt können wir klar erkennen, wann Set() jeweils Monitor.Pulse()/PulseAll() ähnelt:

    • Wenn EventWaitHandle im AutoReset-Modus arbeitet, ähnelt Set() hinsichtlich der Weckfunktion Monitor.Pulse(). Zu diesem Zeitpunkt kann Set() nur einen von vielen (wenn mehrere) blockierten Threads aufwecken. Es gibt jedoch immer noch einige Unterschiede zwischen den beiden:

    1. Die Funktion von Set() besteht nicht nur darin, „aufzuwachen“, sondern auch „freizugeben“, sodass der Thread fortgesetzt werden kann funktioniert (fortfahren); im Gegenteil, der durch Pulse() geweckte Thread wechselt nur wieder in den Running-Zustand und nimmt am Wettbewerb um die Objektsperre teil. Niemand kann garantieren, dass er die Objektsperre erhält.

    2. Der aufgerufene Zustand von Pulse() wird nicht beibehalten. Wenn daher Pulse() aufgerufen wird, obwohl keine wartenden Threads vorhanden sind, wird der nächste Thread, der Monitor.Wait() aufruft, trotzdem blockiert, als ob Pulse() nie aufgerufen worden wäre. Mit anderen Worten: Monitor.Pulse() funktioniert nur, wenn es aufgerufen wird, im Gegensatz zu Set(), das bis zum nächsten WaitXXX() anhält.

  • Wenn die Set()-Methode eines im ManualReset-Modus arbeitenden EventWaitHandle aufgerufen wird, ähnelt seine Weckfunktion der von Monitor.PulseAll() Empfange das Signal und werde geweckt. Der Unterschied zwischen den beiden ist genau der gleiche wie oben.

  • Werfen wir einen Blick auf die anderen Konstruktoren von EventWaitHandle:

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name): Die ersten beiden Parameter haben wir bereits gesehen, und der dritte Parametername wird verwendet, um Synchronisierungsereignisse systemweit anzugeben Name. Ja, wie wir im Mutex-Artikel erwähnt haben, können wir ein globales EventWaitHandle erstellen und es später für prozessübergreifende Benachrichtigungen verwenden, da die übergeordnete Klasse WaitHandle wie Mutex prozessdomänenübergreifend arbeiten kann. Beachten Sie, dass beim Namen immer noch die Groß-/Kleinschreibung beachtet wird und es immer noch Probleme mit dem Namenspräfix gibt, die Sie hier nachlesen können. Dies entspricht dem Erstellen eines lokalen, unbenannten EventWaitHandle, wenn der Name null oder ein leerer string ist. Dennoch ist es möglich, dass nur eine Instanz zurückgegeben wird, um das EventWaitHandle mit demselben Namen darzustellen, da im System bereits ein EventWaitHandle mit demselben Namen vorhanden ist. Letztendlich ist es also immer noch dasselbe: Wenn Sie zuerst wissen möchten, ob dieses EventWaitHandle von Ihnen erstellt wurde, müssen Sie einen der beiden folgenden Konstruktoren verwenden.

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out BooleancreatedNew):createdNew wird verwendet, um anzugeben, ob das EventWaitHandle erfolgreich erstellt wurde, true zeigt den Erfolg an , false Zeigt an, dass bereits ein Ereignis mit demselben Namen vorhanden ist.

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out Boolean CreatingNew, EventWaitHandleSecurity): Bezüglich Sicherheit Problemen schauen Sie sich bitte direkt das Beispiel für diesen Konstruktor an. Den Sicherheitsproblemen des globalen MutexEventWaitHandle sollte mehr Aufmerksamkeit geschenkt werden als denen von Mutex, da es für Hacker möglich ist, denselben Ereignisnamen zum Senden von Signalen oder zum Organisieren Ihrer Threads zu verwenden, was Ihrer Geschäftslogik ernsthaft schaden kann.

    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();
        }
    }


    Das obige ist der detaillierte Inhalt von.NET-Synchronisierung und asynchrones EventWaitHandle. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn