585 Stimmen

Was ist der Unterschied zwischen ManualResetEvent und AutoResetEvent in .NET?

Ich habe die Dokumentation dazu gelesen und glaube, dass ich es verstanden habe. Eine AutoResetEvent wird zurückgesetzt, wenn der Code durchläuft event.WaitOne() sondern ein ManualResetEvent nicht.

Ist dies richtig?

16voto

vezenkov Punkte 3649

autoResetEvent.WaitOne()

ist vergleichbar mit

try
{
   manualResetEvent.WaitOne();
}
finally
{
   manualResetEvent.Reset();
}

als eine atomare Operation

13voto

Teoman shipahi Punkte 45327

OK, normalerweise ist es keine gute Praxis, 2 Antworten im selben Thread hinzuzufügen, aber ich wollte meine vorherige Antwort nicht bearbeiten/löschen, da sie auf andere Weise helfen kann.

Jetzt habe ich einen viel umfangreicheren und leicht verständlichen Ausschnitt aus der Konsolenanwendung zum Ausführen und Lernen erstellt.

Lassen Sie die Beispiele einfach auf zwei verschiedenen Konsolen laufen und beobachten Sie das Verhalten. Sie werden dort eine viel klarere Vorstellung davon bekommen, was hinter den Kulissen passiert.

Manueller Reset Ereignis

using System;
using System.Threading;

namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
    public class ManualResetEventSample
    {
        private readonly ManualResetEvent _manualReset = new ManualResetEvent(false);

        public void RunAll()
        {
            new Thread(Worker1).Start();
            new Thread(Worker2).Start();
            new Thread(Worker3).Start();
            Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
            Thread.Sleep(15000);
            Console.WriteLine("1- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("2- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("3- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("4- Main will call ManualResetEvent.Reset() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("It ran one more time. Why? Even Reset Sets the state of the event to nonsignaled (false), causing threads to block, this will initial the state, and threads will run again until they WaitOne().");
            Thread.Sleep(10000);
            Console.WriteLine();
            Console.WriteLine("This will go so on. Everytime you call Set(), ManualResetEvent will let ALL threads to run. So if you want synchronization between them, consider using AutoReset event, or simply user TPL (Task Parallel Library).");
            Thread.Sleep(5000);
            Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

        }

        public void Worker1()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker1 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker2()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker2 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker3()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker3 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

}

Manual Reset Event Output

Auto-Reset-Ereignis

using System;
using System.Threading;

namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
    public class AutoResetEventSample
    {
        private readonly AutoResetEvent _autoReset = new AutoResetEvent(false);

        public void RunAll()
        {
            new Thread(Worker1).Start();
            new Thread(Worker2).Start();
            new Thread(Worker3).Start();
            Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
            Thread.Sleep(15000);
            Console.WriteLine("1- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("2- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("3- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("4- Main will call AutoResetEvent.Reset() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("Nothing happened. Why? Becasuse Reset Sets the state of the event to nonsignaled, causing threads to block. Since they are already blocked, it will not affect anything.");
            Thread.Sleep(10000);
            Console.WriteLine("This will go so on. Everytime you call Set(), AutoResetEvent will let another thread to run. It will make it automatically, so you do not need to worry about thread running order, unless you want it manually!");
            Thread.Sleep(5000);
            Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

        }

        public void Worker1()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker1 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker2()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker2 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker3()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker3 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

}

Auto Reset Event Output

9voto

Masoud Siahkali Punkte 4465

AutoResetEvent verwaltet eine boolesche Variable im Speicher. Wenn die boolesche Variable false ist, wird der Thread blockiert, und wenn die boolesche Variable true ist, wird der Thread freigegeben.

Wenn wir ein AutoResetEvent-Objekt instanziieren, übergeben wir im Konstruktor den Standardwert boolean value. Im Folgenden wird die Syntax für die Instanziierung eines AutoResetEvent-Objekts beschrieben.

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

WaitOne-Methode

Diese Methode blockiert den aktuellen Thread und wartet auf das Signal des anderen Threads. Die WaitOne-Methode versetzt den aktuellen Thread in einen Sleep-Thread-Zustand. Die WaitOne-Methode gibt true zurück, wenn sie das Signal empfängt, andernfalls gibt sie false zurück.

autoResetEvent.WaitOne();

Zweite Überladung der WaitOne-Methode wartet auf die angegebene Anzahl von Sekunden. Wenn er kein Signal erhält, setzt der Thread seine Arbeit fort.

static void ThreadMethod()
{
    while(!autoResetEvent.WaitOne(TimeSpan.FromSeconds(2)))
    {
        Console.WriteLine("Continue");
        Thread.Sleep(TimeSpan.FromSeconds(1));
    }

    Console.WriteLine("Thread got signal");
}

Wir rufen die Methode WaitOne auf, indem wir die 2 Sekunden als Argumente übergeben. In der while-Schleife wird 2 Sekunden lang auf das Signal gewartet, dann wird die Arbeit fortgesetzt. Wenn der Thread das Signal erhalten hat, gibt WaitOne den Wert true zurück, verlässt die Schleife und druckt die Meldung "Thread got signal".

Methode einstellen

Die Methode AutoResetEvent Set sendet das Signal an den wartenden Thread, um seine Arbeit fortzusetzen. Nachfolgend ist die Syntax für den Aufruf der Methode Set dargestellt.

autoResetEvent.Set();

ManualResetEvent verwaltet eine boolesche Variable im Speicher. Wenn die boolesche Variable false ist, werden alle Threads blockiert, und wenn die boolesche Variable true ist, werden alle Threads freigegeben.

Wenn wir ein ManualResetEvent instanziieren, initialisieren wir es mit einem booleschen Standardwert.

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

Im obigen Code initialisieren wir das ManualResetEvent mit dem Wert false, d.h. alle Threads, die die WaitOne-Methode aufrufen, blockieren, bis ein Thread die Set()-Methode aufruft.

Wenn wir ManualResetEvent mit dem Wert true initialisieren, werden alle Threads, die die WaitOne-Methode aufrufen, nicht blockiert und sind frei, um weiterzumachen.

WaitOne-Methode

Diese Methode blockiert den aktuellen Thread und wartet auf das Signal des anderen Threads. Sie gibt true zurück, wenn sie ein Signal erhält, sonst false.

Nachfolgend ist die Syntax für den Aufruf der WaitOne-Methode aufgeführt.

manualResetEvent.WaitOne();

In der zweiten Überladung der WaitOne-Methode können wir das Zeitintervall angeben, bis der aktuelle Thread auf das Signal wartet. Wenn innerhalb der internen Zeit kein Signal empfangen wird, wird false zurückgegeben und die nächste Zeile der Methode aufgerufen.

Nachfolgend ist die Syntax für den Aufruf der Methode WaitOne mit Zeitintervall dargestellt.

bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));

Wir haben 5 Sekunden in der WaitOne-Methode angegeben. Wenn das Objekt manualResetEvent innerhalb von 5 Sekunden kein Signal empfängt, setzt es die Variable isSignalled auf false.

Methode einstellen

Diese Methode wird verwendet, um das Signal an alle wartenden Threads zu senden. Set() Die Methode setzt die boolesche Variable des ManualResetEvent-Objekts auf true. Alle wartenden Threads werden entsperrt und können weiterarbeiten.

Nachfolgend ist die Syntax für den Aufruf der Methode Set() aufgeführt.

manualResetEvent.Set();

Reset-Methode

Sobald wir die Set()-Methode für das ManualResetEvent-Objekt aufrufen, bleibt sein Boolescher Wert wahr. Um den Wert zurückzusetzen, können wir die Methode Reset() verwenden. Die Reset-Methode ändert den booleschen Wert in false.

Im Folgenden finden Sie die Syntax für den Aufruf der Methode Reset.

manualResetEvent.Reset();

Wir müssen die Reset-Methode sofort nach dem Aufruf der Set-Methode aufrufen, wenn wir das Signal mehrmals an Threads senden wollen.

7voto

Boaz Punkte 23533

Ja, das ist absolut richtig.

Man könnte ManualResetEvent als eine Möglichkeit sehen, den Zustand anzuzeigen. Etwas ist eingeschaltet (Set) oder ausgeschaltet (Reset). Ein Ereignis mit einer gewissen Dauer. Jeder Thread, der auf diesen Zustand wartet, kann fortfahren.

Ein AutoResetEvent ist eher mit einem Signal vergleichbar. Ein einmaliger Hinweis darauf, dass etwas passiert ist. Ein Ereignis ohne jegliche Dauer. Typischerweise, aber nicht notwendigerweise, ist das "Etwas", das passiert ist, klein und muss von einem einzigen Thread verarbeitet werden - daher die automatische Rücksetzung, nachdem ein einzelner Thread das Ereignis konsumiert hat.

7voto

Swapnil Patil Punkte 146

Ja, das ist richtig.

Sie können sich anhand der Verwendung dieser beiden Begriffe ein Bild machen.

Wenn Sie mitteilen müssen, dass Sie mit einer Arbeit fertig sind und andere (Threads), die darauf warten, nun fortfahren können, sollten Sie ManualResetEvent verwenden.

Wenn Sie einen gegenseitigen exklusiven Zugriff auf eine Ressource benötigen, sollten Sie AutoResetEvent verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X