Ich habe die Dokumentation dazu gelesen und glaube zu verstehen. A wird AutoResetEvent
zurückgesetzt, wenn der Code durchlaufen event.WaitOne()
wird, a ManualResetEvent
jedoch nicht.
Ist das richtig?
Ich habe die Dokumentation dazu gelesen und glaube zu verstehen. A wird AutoResetEvent
zurückgesetzt, wenn der Code durchlaufen event.WaitOne()
wird, a ManualResetEvent
jedoch nicht.
Ist das richtig?
Antworten:
Ja. Es ist wie der Unterschied zwischen einer Mautstelle und einer Tür. Das ManualResetEvent
ist die Tür, die manuell geschlossen (zurückgesetzt) werden muss. Das AutoResetEvent
ist eine Mautstelle, an der ein Auto vorbeifahren und automatisch schließen kann, bevor das nächste durchkommt.
Stellen Sie sich vor, das wird AutoResetEvent
ausgeführt WaitOne()
und Reset()
als einzelne atomare Operation.
Die kurze Antwort lautet ja. Der wichtigste Unterschied besteht darin, dass mit einem AutoResetEvent nur ein einziger wartender Thread fortgesetzt werden kann. Ein ManualResetEvent hingegen ermöglicht es Threads, mehrere gleichzeitig, fortzufahren, bis Sie anweisen, dass sie gestoppt werden sollen (Zurücksetzen).
Entnommen aus dem C # 3.0 Nutshell-Buch von Joseph Albahari
Threading in C # - Kostenloses E-Book
Ein ManualResetEvent ist eine Variation von AutoResetEvent. Es unterscheidet sich darin, dass es nicht automatisch zurückgesetzt wird, nachdem ein Thread bei einem WaitOne-Aufruf durchgelassen wurde, und daher wie ein Gate funktioniert: Das Aufrufen von Set öffnet das Gate und lässt eine beliebige Anzahl von Threads zu, die WaitOne am Gate durchlässt. Durch das Aufrufen von Reset wird das Gate geschlossen, wodurch sich möglicherweise eine Warteschlange von Kellnern ansammelt, bis es das nächste Mal geöffnet wird.
Man könnte diese Funktionalität mit einem booleschen "gateOpen" -Feld (deklariert mit dem flüchtigen Schlüsselwort) in Kombination mit "Spin-Sleeping" simulieren - wiederholt das Flag überprüfen und dann für einen kurzen Zeitraum schlafen.
ManualResetEvents werden manchmal verwendet, um zu signalisieren, dass ein bestimmter Vorgang abgeschlossen ist oder dass die Initialisierung eines Threads abgeschlossen ist und zur Ausführung von Arbeiten bereit ist.
Ich habe einfache Beispiele erstellt, um das Verständnis von ManualResetEvent
vs zu verdeutlichen AutoResetEvent
.
AutoResetEvent
: Nehmen wir an, Sie haben 3 Worker-Thread. Wenn einer dieser Threads WaitOne()
alle anderen 2 Threads aufruft, wird die Ausführung gestoppt und auf das Signal gewartet. Ich gehe davon aus, dass sie verwenden WaitOne()
. Es ist wie; Wenn ich nicht arbeite, arbeitet niemand. Im ersten Beispiel können Sie das sehen
autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();
Wenn Sie aufrufen Set()
, funktionieren alle Threads und warten auf das Signal. Nach 1 Sekunde sende ich ein zweites Signal und sie werden ausgeführt und warten ( WaitOne()
). Denken Sie daran, dass diese Jungs Fußballspieler sind und wenn ein Spieler sagt, ich werde warten, bis der Manager mich anruft, und andere warten, bis der Manager ihnen sagt, dass sie fortfahren sollen ( Set()
)
public class AutoResetEventSample
{
private AutoResetEvent autoReset = new AutoResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();
Console.WriteLine("Main thread reached to end.");
}
public void Worker1()
{
Console.WriteLine("Entered in worker 1");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker1 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
public void Worker2()
{
Console.WriteLine("Entered in worker 2");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker2 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
public void Worker3()
{
Console.WriteLine("Entered in worker 3");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker3 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
}
In diesem Beispiel können Sie deutlich sehen, dass beim ersten Treffer Set()
alle Threads losgelassen werden und nach 1 Sekunde alle Threads zum Warten aufgefordert werden! Sobald Sie sie erneut einstellen, unabhängig davon, ob sie im Haus anrufen WaitOne()
, werden sie weiter ausgeführt, da Sie manuell anrufen müssen Reset()
, um sie alle zu stoppen.
manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();
Es geht mehr um die Beziehung zwischen Schiedsrichter und Spieler, unabhängig davon, ob einer der Spieler verletzt ist oder ob das Warten auf das Spielen anderer weiterhin funktioniert. Wenn der Schiedsrichter wait ( Reset()
) sagt, warten alle Spieler bis zum nächsten Signal.
public class ManualResetEventSample
{
private ManualResetEvent manualReset = new ManualResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();
Console.WriteLine("Main thread reached to end.");
}
public void Worker1()
{
Console.WriteLine("Entered in worker 1");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker1 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
public void Worker2()
{
Console.WriteLine("Entered in worker 2");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker2 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
public void Worker3()
{
Console.WriteLine("Entered in worker 3");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker3 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
}
autoResetEvent.WaitOne()
ist ähnlich wie
try
{
manualResetEvent.WaitOne();
}
finally
{
manualResetEvent.Reset();
}
als atomare Operation
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 dies auf andere Weise hilfreich sein kann.
Jetzt habe ich unten ein viel umfassenderes und leicht verständliches Run-to-Learn-Konsolen-App-Snippet erstellt.
Führen Sie die Beispiele einfach auf zwei verschiedenen Konsolen aus und beobachten Sie das Verhalten. Sie werden dort eine viel klarere Vorstellung davon bekommen, was hinter den Kulissen passiert.
Manuelles Zurücksetzen
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);
}
}
}
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);
}
}
}
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 entsperrt.
Wenn wir ein AutoResetEvent-Objekt instanziieren, übergeben wir den Standardwert des booleschen Werts im Konstruktor. Unten finden Sie die Syntax zum Instanziieren eines AutoResetEvent-Objekts.
AutoResetEvent autoResetEvent = new AutoResetEvent(false);
WaitOne-Methode
Diese Methode blockiert den aktuellen Thread und wartet auf das Signal eines anderen Threads. Die WaitOne-Methode versetzt den aktuellen Thread in einen Ruhezustand. Die WaitOne-Methode gibt true zurück, wenn sie das Signal empfängt. Andernfalls wird false zurückgegeben.
autoResetEvent.WaitOne();
Die zweite Überladung der WaitOne-Methode wartet auf die angegebene Anzahl von Sekunden. Wenn es keinen Signal-Thread erhält, setzt es 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 haben die WaitOne-Methode aufgerufen, indem wir die 2 Sekunden als Argumente übergeben haben. In der while-Schleife wartet es 2 Sekunden auf das Signal und setzt dann seine Arbeit fort. Wenn der Thread das Signal erhalten hat, gibt WaitOne true zurück und verlässt die Schleife und gibt das "Thread got signal" aus.
Methode einstellen
Die AutoResetEvent Set-Methode hat das Signal an den wartenden Thread gesendet, um seine Arbeit fortzusetzen. Unten finden Sie die Syntax zum Aufrufen der Set-Methode.
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 entsperrt.
Wenn wir ein ManualResetEvent instanziieren, initialisieren wir es mit dem Standard-Booleschen Wert.
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
Im obigen Code initialisieren wir das ManualResetEvent mit einem falschen Wert. Dies bedeutet, dass alle Threads, die die WaitOne-Methode aufrufen, blockiert werden, 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 können fortfahren.
WaitOne-Methode
Diese Methode blockiert den aktuellen Thread und wartet auf das Signal eines anderen Threads. Es gibt true zurück, wenn es ein Signal empfängt, andernfalls false.
Unten finden Sie die Syntax zum Aufrufen der WaitOne-Methode.
manualResetEvent.WaitOne();
In der zweiten Überladung der WaitOne-Methode können wir das Zeitintervall angeben, bis der aktuelle Thread auf das Signal wartet. Wenn es innerhalb der internen Zeit kein Signal empfängt, gibt es false zurück und geht in die nächste Methodenzeile.
Unten finden Sie die Syntax zum Aufrufen der WaitOne-Methode mit Zeitintervall.
bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
Wir haben 5 Sekunden in der WaitOne-Methode angegeben. Wenn das manualResetEvent-Objekt zwischen 5 Sekunden kein Signal empfängt, setzt es die Variable isSignalled auf false.
Methode einstellen
Diese Methode wird zum Senden des Signals an alle wartenden Threads verwendet. Set () -Methode Setzen Sie die boolesche Variable des ManualResetEvent-Objekts auf true. Alle wartenden Threads werden entsperrt und fahren fort.
Unten finden Sie die Syntax zum Aufrufen der Set () -Methode.
manualResetEvent.Set();
Methode zurücksetzen
Sobald wir die Set () -Methode für das ManualResetEvent-Objekt aufrufen, bleibt ihr Boolescher Wert true. Um den Wert zurückzusetzen, können wir die Reset () -Methode verwenden. Die Methode zum Zurücksetzen ändert den booleschen Wert in false.
Unten finden Sie die Syntax zum Aufrufen der Reset-Methode.
manualResetEvent.Reset();
Wir müssen die Reset-Methode sofort nach dem Aufruf der Set-Methode aufrufen, wenn wir mehrmals ein Signal an Threads senden möchten.
Ja. Das ist absolut richtig.
Sie können ManualResetEvent als eine Möglichkeit sehen, den Status anzuzeigen. Etwas ist ein (Set) oder aus (Reset). Ein Ereignis mit einer gewissen Dauer. Jeder Thread, der auf diesen Status wartet, kann fortfahren.
Ein AutoResetEvent ist eher mit einem Signal vergleichbar. Ein One-Shot-Hinweis darauf, dass etwas passiert ist. Ein Ereignis ohne Dauer. Normalerweise, aber nicht unbedingt, ist das "Etwas", das passiert ist, klein und muss von einem einzelnen Thread verarbeitet werden - daher das automatische Zurücksetzen, nachdem ein einzelner Thread das Ereignis verbraucht hat.
Ja, das ist richtig.
Durch die Verwendung dieser beiden können Sie sich ein Bild machen.
Wenn Sie feststellen müssen, dass Sie mit einigen Arbeiten fertig sind und andere (Threads) darauf warten können, sollten Sie ManualResetEvent verwenden.
Wenn Sie gegenseitigen exklusiven Zugriff auf eine Ressource benötigen, sollten Sie AutoResetEvent verwenden.
Wenn Sie AutoResetEvent und ManualResetEvent verstehen möchten, müssen Sie nicht Threading, sondern Interrupts verstehen!
.NET möchte Low-Level-Programmierung so weit wie möglich heraufbeschwören.
Ein Interrupt wird in der Low-Level-Programmierung verwendet und entspricht einem Signal, das von Low zu High wurde (oder umgekehrt). In diesem Fall unterbricht das Programm seine normale Ausführung und bewegt den Ausführungszeiger auf die Funktion, die dieses Ereignis behandelt .
Das erste, was zu tun ist, wenn ein Interrupt passiert ist, ist das Zurücksetzen seines Status, da die Hardware folgendermaßen funktioniert:
Dies ist der Unterschied zwischen ManualResetEvent und AutoResetEvent.
Wenn ein ManualResetEvent auftritt und ich es nicht zurücksetze, kann ich es beim nächsten Mal nicht anhören.