Teilen über


EventWaitHandle

Die EventWaitHandle Klasse ermöglicht es Threads, miteinander zu kommunizieren, indem signalisiert und auf Signale gewartet wird. Ereigniswarteschlüssel (auch einfach als Ereignisse bezeichnet) sind Warteschlüssel, die signalisiert werden können, um einen oder mehrere wartende Threads freizugeben. Nachdem ein Ereignis-Wait-Handle als Signal verwendet wurde, wird es entweder manuell oder automatisch zurückgesetzt. Die EventWaitHandle-Klasse kann entweder ein lokales Ereigniswartehandle (lokales Ereignis) oder ein benanntes Systemereigniswartehandle (benanntes Ereignis oder Systemereignis, das für alle Prozesse sichtbar ist) darstellen.

Hinweis

Ereignis-Wait-Handles sind keine .NET-Ereignisse. Es sind keine Delegaten oder Ereignishandler beteiligt. Das Wort "Event" wird verwendet, um sie zu beschreiben, da sie traditionell als Betriebssystemereignisse bezeichnet wurden, und weil die Signalisierung des Wartehandles den wartenden Threads anzeigt, dass ein Event eingetreten ist.

Sowohl lokale als auch benannte Ereignis-Wait-Handles verwenden Synchronisierungsobjekte des Systems, die durch SafeWaitHandle-Wrapper geschützt sind, um sicherzustellen, dass die Ressourcen freigegeben werden. Sie können die Dispose Methode verwenden, um die Ressourcen sofort freizugeben, wenn Sie die Verwendung des Objekts abgeschlossen haben.

Ereignis-Wait-Handles mit automatischer Rücksetzung

Sie erstellen ein ereignis für das automatische Zurücksetzen, indem Sie angeben EventResetMode.AutoReset , wann Sie das EventWaitHandle Objekt erstellen. Wie der Name schon sagt, wird dieses Synchronisierungsereignis nach der Verwendung als Signal automatisch zurückgesetzt, nachdem ein einzelner wartender Thread freigegeben wurde. Signalisieren Sie das Ereignis durch Aufrufen der Set Methode.

Um den exklusiven Zugriff auf eine Ressource für einen einzelnen Thread zu einem bestimmten Zeitpunkt sicherzustellen, werden Ereignisse mit automatischer Rücksetzung verwendet. Ein Thread fordert die Ressource durch Aufrufen der WaitOne Methode an. Wenn kein anderer Thread das Wartehandle hält, gibt die Methode zurück true , und der aufrufende Thread hat die Kontrolle über die Ressource.

Von Bedeutung

Wie bei allen Synchronisierungsmechanismen müssen Sie sicherstellen, dass alle Codepfade auf das entsprechende Wartezeithandle warten, bevor Sie auf eine geschützte Ressource zugreifen. Die Threadsynchronisierung ist kooperativ.

Wenn ein automatisches Reset-Ereignis signalisiert wird, wenn keine Threads warten, bleibt es signalisiert, bis ein Thread versucht, darauf zu warten. Das Ereignis gibt den Thread frei, setzt ihn sofort zurück und blockiert dabei nachfolgende Threads.

Ereignis-Wait-Handles mit manueller Rücksetzung

Sie erstellen ein manuelles Zurücksetzungsereignis, indem Sie EventResetMode.ManualReset angeben, wenn Sie das EventWaitHandle-Objekt erstellen. Wie der Name schon sagt, muss dieses Synchronisierungsereignis manuell zurückgesetzt werden, nachdem es signalisiert wurde. Bis das Ereignis durch Aufrufen der Reset-Methode zurückgesetzt wird, können Threads, die auf das Ereignishandle warten, sofort fortgesetzt werden, ohne blockiert zu werden.

Ein Ereignis mit manueller Rücksetzung funktioniert wie das Gatter einer Pferdekoppel. Wenn das Ereignis nicht als Signal verwendet wird, sind Threads, die darauf warten, blockiert, also sozusagen wie Pferde auf einer Koppel eingesperrt. Wenn das Ereignis durch Aufrufen der Set-Methode als Signal verwendet wird, werden alle Threads befreit und können fortgesetzt werden. Das Ereignis bleibt signalisiert, bis die Reset Methode aufgerufen wird. Dadurch wird das manuelle Reset-Ereignis zu einem idealen Weg zum Halten von Threads, die warten müssen, bis ein Thread eine Aufgabe beendet.

Wie bei Pferden auf einer Koppel dauert es eine Zeitlang, bis die freigegebenen Threads vom Betriebssystem geplant werden und die Ausführung fortgesetzt wird. Wenn die Reset Methode aufgerufen wird, bevor alle Threads die Ausführung fortgesetzt haben, blockieren die verbleibenden Threads erneut. Welche Threads fortgesetzt werden und welche Threads blockieren, hängt von zufälligen Faktoren wie der Auslastung des Systems, der Anzahl der Threads ab, die auf den Zeitplan warten usw. Dies ist kein Problem, wenn der Thread, der das Ereignis signalisiert, nach der Signalisierung endet, was das am häufigsten verwendete Verwendungsmuster ist. Wenn Sie möchten, dass der Thread, der das Ereignis als Signal verwendet hat, nach dem Fortsetzen aller wartenden Threads einen neuen Task beginnt, müssen Sie den Thread blockieren, bis alle wartenden Threads fortgesetzt wurden. Andernfalls haben Sie eine Wettlaufbedingung, und das Verhalten Ihres Codes ist unvorhersehbar.

Features, die für automatische und manuelle Ereignisse üblich sind

In der Regel werden ein oder mehrere Threads auf einem EventWaitHandle blockiert, bis ein nicht blockierter Thread die Set-Methode aufruft, die einen der wartenden Threads (im Falle eines automatischen Zurücksetzungsereignisses) oder alle (im Falle eines manuellen Zurücksetzungsereignisses) freigibt. Ein Thread kann ein EventWaitHandle als Signal verwenden und dann durch Aufrufen der statischen WaitHandle.SignalAndWait-Methode als atomischer Vorgang blockiert werden.

EventWaitHandle Objekte können mit statischen WaitHandle.WaitAll Und WaitHandle.WaitAny Methoden verwendet werden. Da die EventWaitHandle- und Mutex-Klassen beide von WaitHandle abgeleitet sind, können Sie beide Klassen mit diesen Methoden verwenden.

Benannte Ereignisse

Das Windows-Betriebssystem ermöglicht es, Event-Wait-Handles zu benennen. Ein benanntes Ereignis ist systemweit. Das heißt, nachdem das benannte Ereignis erstellt wurde, ist es für alle Threads in allen Prozessen sichtbar. So können benannte Ereignisse verwendet werden, um die Aktivitäten von Prozessen sowie Threads zu synchronisieren.

Sie können ein EventWaitHandle Objekt erstellen, das ein benanntes Systemereignis darstellt, indem Sie einen der Konstruktoren verwenden, der einen Ereignisnamen angibt.

Hinweis

Da benannte Ereignisse systemweit sind, können mehrere EventWaitHandle Objekte vorhanden sein, die dasselbe benannte Ereignis darstellen. Jedes Mal, wenn Sie einen Konstruktor oder die OpenExisting Methode aufrufen, wird ein neues EventWaitHandle Objekt erstellt. Wenn Sie denselben Namen angeben, werden wiederholt mehrere Objekte erstellt, die dasselbe benannte Ereignis darstellen.

Vorsicht wird bei der Verwendung benannter Ereignisse empfohlen. Da sie systemweit sind, kann ein anderer Prozess, der denselben Namen verwendet, Ihre Threads unerwartet blockieren. Bösartiger Code, der auf demselben Computer ausgeführt wird, kann dies als Grundlage eines Denial-of-Service-Angriffs verwenden.

Verwenden Sie die Zugriffssteuerungssicherheit, um ein EventWaitHandle Objekt zu schützen, das ein benanntes Ereignis darstellt, vorzugsweise mithilfe eines Konstruktors, der ein EventWaitHandleSecurity Objekt angibt. Sie können auch die Zugriffssteuerungssicherheit mit der Methode SetAccessControl anwenden, jedoch bleibt zwischen dem Zeitpunkt, zu dem das Ereigniswartehandle erstellt wird, und dem Zeitpunkt, zu dem es geschützt wird, eine Sicherheitslücke bestehen. Ereignisse durch Sicherheitsvorkehrungen für die Zugangskontrolle zu schützen hilft dabei, böswillige Angriffe zu verhindern, aber es löst nicht das Problem unbeabsichtigter Namenskonflikte.

Hinweis

Im Gegensatz zur Klasse EventWaitHandle können die abgeleiteten Klassen AutoResetEvent und ManualResetEvent nur lokale Wartezeithandles darstellen. Sie können keine benannten Systemereignisse darstellen.

Siehe auch