Freigeben über


Semaphor und SemaphoreSlim

Die System.Threading.Semaphore Klasse stellt einen benannten (systemweiten) oder lokalen Semaphor dar. Es ist ein dünner Wrapper um das Win32-Semaphorobjekt. Win32-Zählsemaphore sind Semaphore, die den Zugriff auf einen Ressourcenpool steuern.

Die SemaphoreSlim Klasse stellt ein leichtgewichtiges, schnelles Semaphor dar, das verwendet werden kann, um innerhalb eines einzelnen Prozesses zu warten, wenn kurze Wartezeiten erwartet werden. Während der Spin-Wait-Phase dreht sich die CPU aktiv – sie ist nicht im Leerlauf. Wie kurz "kurz" sein muss, hängt von der Art der Wartezeit ab: Wenn Threads mit CPU-Ressourcen konkurrieren, verbraucht der sich drehende Thread CPU-Zeit, sodass die Wartezeit sehr kurz sein sollte, gemessen in Mikrosekunden. Wenn die Wartezeit auf eine nicht CPU-gebundene Ressource (z. B. E/A) liegt, ist der Spin-Wait-Aufwand weniger bedenklich, und die akzeptable Wartezeit kann etwas länger sein, gemessen in Millisekunden. Wenn Wartezeiten unvorhersehbar sind oder signifikant sein könnten, verwenden Sie stattdessen System.Threading.Semaphore, das sich nicht dreht. SemaphoreSlim basiert so weit wie möglich auf Synchronisierungsgrundtypen, die von der Common Language Runtime (CLR) bereitgestellt werden. Es stellt jedoch auch verzögert initialisierte, Kernel-basierte Wait-Handles zur Unterstützung des Wartens auf mehrere Semaphoren bereit. SemaphoreSlim unterstützt auch die Verwendung von Abbruchtokens, unterstützt aber keine benannten Semaphore oder die Verwendung eines Wait-Handles für die Synchronisierung.

Verwalten einer begrenzten Ressource

Threads geben das Semaphor ein, indem je nach Semaphortyp verschiedene Methoden aufgerufen werden. Rufen Sie für ein System.Threading.Semaphore Objekt die WaitOne Methode auf (geerbt von WaitHandle). Rufen Sie für ein SemaphoreSlim-Objekt die Methode SemaphoreSlim.Wait oder SemaphoreSlim.WaitAsync auf. Wenn der Aufruf zurückgegeben wird, wird der Zähler des Semaphors dekrementiert. Wenn ein Thread den Zugang anfordert und die Anzahl null ist, wird der Thread blockiert. Wenn Threads den Semaphor freigeben, indem sie die Semaphore.Release- oder SemaphoreSlim.Release-Methode aufrufen, können blockierte Threads eintreten. Keine garantierte Reihenfolge, wie z.B. First-In, First-Out (FIFO) oder Last-In, First-Out (LIFO), steuert, welcher der blockierten Threads als nächstes in das Semaphor eintritt.

Ein Thread kann das Semaphor mehrmals eingeben, indem die System.Threading.Semaphore Methode des WaitOne Objekts oder die SemaphoreSlim Methode des Wait Objekts wiederholt aufgerufen wird. Um das Semaphor freizugeben, rufen Sie entweder die Semaphore.Release()- oder die SemaphoreSlim.Release()-Methode genauso oft auf, wie der Thread eingetreten ist. Rufen Sie alternativ die Semaphore.Release(Int32) oder SemaphoreSlim.Release(Int32) Überladung auf, und geben Sie die Anzahl der freizugebenden Einträge an.

Semaphore und Threadidentität

Die beiden Semaphortypen erzwingen keine Threadidentität für Aufrufe der WaitOne, Wait, , Releaseund SemaphoreSlim.Release Methoden. Ein häufiges Verwendungsszenario für Semaphoren umfasst beispielsweise einen Producerthread und einem Consumerthread, wobei ein Thread den Zähler des Semaphors immer erhöht und der andere ihn immer verringert.

Stellen Sie sicher, dass ein Thread das Semaphor nicht zu oft freigibt. Angenommen, ein Semaphor hat eine maximale Anzahl von zwei, und Thread A und Thread B betreten beide das Semaphor. Wenn ein Programmierfehler in Thread B dazu führt, dass Release zweimal aufgerufen wird, sind beide Aufrufe erfolgreich. Der Zähler des Semaphors ist voll, und wenn Thread A schließlich Release aufruft, wird eine SemaphoreFullException ausgelöst.

Benannte Semaphoren

Das Windows-Betriebssystem ermöglicht Semaphoren, Namen zu haben. Ein benanntes Semaphor ist systemweit – sobald es erstellt wurde, ist es für alle Threads in allen Prozessen sichtbar. Benannte Semaphore können daher die Aktivitäten von Prozessen sowie Threads synchronisieren.

Erstellen Sie ein Semaphore Objekt, das einen benannten System-Semaphor darstellt, indem Sie einen der Konstruktoren verwenden, der einen Namen angibt.

Von Bedeutung

Da benannte Semaphore systemweit sind, ist es möglich, mehrere Semaphore Objekte zu haben, die denselben benannten Semaphor darstellen. Jedes Mal, wenn Sie einen Konstruktor oder die Semaphore.OpenExisting Methode aufrufen, wird ein neues Semaphore Objekt erstellt. Wenn Sie denselben Namen angeben, werden wiederholt mehrere Objekte erstellt, die denselben benannten Semaphor darstellen.

Vorsicht ist geboten, wenn Sie benannte Semaphoren verwenden. Da sie systemweit sind, kann ein anderer Prozess, der denselben Namen verwendet, unerwartet Ihren Semaphor eingeben. 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 Semaphore Objekt zu schützen, das einen benannten Semaphor darstellt, vorzugsweise mithilfe eines Konstruktors, der ein System.Security.AccessControl.SemaphoreSecurity Objekt angibt. Sie können auch Zugriffskontrolle mit der Semaphore.SetAccessControl Methode anwenden, aber dadurch bleibt ein Sicherheitsfenster zwischen der Erstellung des Semaphors und dem Zeitpunkt, zu dem es geschützt ist. Der Schutz von Semaphoren durch Zugriffskontrolle hilft, böswillige Angriffe zu verhindern, löst jedoch nicht das Problem unbeabsichtigter Namenskollisionen.

Siehe auch