Semaphore und SemaphoreSlim

Die System.Threading.Semaphore-Klasse stellt ein benanntes (systemweites) oder lokales Semaphor dar. Dabei handelt es sich um einen einfachen Wrapper um das Win32-Semaphorobjekt. Win32-Semaphoren sind zählende Semaphoren, die zum Steuern des Zugriffs auf einen Ressourcenpool verwendet werden können.

Die SemaphoreSlim-Klasse stellt ein einfaches, schnelles Semaphor dar, das zum Warten innerhalb eines einzelnen Prozesses verwendet werden kann, wenn die Wartezeiten voraussichtlich sehr kurz sind. SemaphoreSlim basiert im möglichst hohen Maße auf Synchronisierungsprimitiven, die von 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 Abbruchtoken, unterstützt aber keine benannten Semaphoren oder die Verwendung eines Wait-Handles für die Synchronisierung.

Verwalten einer beschränkten Ressource

Threads wechseln in den Semaphor durch Aufrufen der WaitOne-Methode, die von der WaitHandle-Klasse geerbt wird, wenn es sich um ein System.Threading.Semaphore-Objekt handelt, bzw. von der SemaphoreSlim.Wait- oder SemaphoreSlim.WaitAsync-Methode, wenn es sich um ein SemaphoreSlim-Objekt handelt. 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. Da Threads das Semaphor durch Aufrufen von Freigeben der Semaphore.Release- oder SemaphoreSlim.Release-Methode freigeben, erhalten blockierte Threads Zugang. Für die Aufnahme von blockierten Threads in das Semaphor gibt es keine festgelegte Reihenfolge, z. B. First in, First Out (FIFO) oder Last in, First Out (LIFO).

Ein Thread kann in das Semaphor mehrfach durch wiederholtes Aufrufen der System.Threading.Semaphore-Methode des WaitOne-Objekts oder der SemaphoreSlim-Methode des Wait-Objekts wechseln. Um das Semaphor freizugeben, kann der Thread entweder die Semaphore.Release()- oder SemaphoreSlim.Release()-Methodenüberladung aufrufen oder die Semaphore.Release(Int32)- oder SemaphoreSlim.Release(Int32)-Methodenüberladung aufrufen und die Anzahl der freizugebenden Einträge angeben.

Semaphoren und Threadidentität

Die zwei Semaphoren erzwingen keine Threadidentität für Aufrufe der Methoden WaitOne, Wait, Release und SemaphoreSlim.Release. 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.

Der Programmierer ist dafür verantwortlich, sicherzustellen, dass ein Thread das Semaphor nicht zu oft freigibt. Angenommen, ein Semaphor hat einen maximalen Zähler von zwei und Thread A sowie Thread B wechseln beide in 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 es, Semaphoren zu benennen. Ein benanntes Semaphor ist systemweit sichtbar. Das heißt, sobald das benannte Semaphor erstellt wurde, ist es für alle Threads in allen Prozessen sichtbar. Folglich kann ein benanntes Semaphore zum Synchronisieren der Aktivitäten von Prozessen und Threads verwendet werden.

Sie können ein Semaphore-Objekt erstellen, das ein benanntes Systemsemaphor darstellt, indem Sie einen der Konstruktoren verwenden, die einen Namen angeben.

Hinweis

Da benannte Semaphoren systemweit sichtbar sind, ist es möglich, mehrere Semaphore-Objekte zu haben, die dasselbe benannte Semaphor darstellen. Bei jedem Aufruf eines Konstruktors oder der Semaphore.OpenExisting-Methode wird ein neues Semaphore-Objekt erstellt. Wenn Sie wiederholt denselben Namen angeben, werden mehrere Objekte erstellt, die desselbe benannte Semaphor darstellen.

Vorsicht ist geboten, wenn Sie benannte Semaphoren verwenden. Da sie systemweit sichtbar sind, kann ein anderer Prozess, der den gleichen Namen verwendet, unerwartet in das Semaphor wechseln. Böswilliger Code, der auf demselben Computer ausgeführt wird, könnte dies als Grundlage für einen Denial-of-Service-Angriff verwenden.

Verwenden Sie die Zugriffssteuerungssicherheit zum Schutz eines Semaphore-Objekts, das ein benanntes Semaphor darstellt, vorzugsweise unter Verwendung eines Konstruktors, der System.Security.AccessControl.SemaphoreSecurity-Objekt angibt. Sie können die Zugriffssteuerungssicherheit auch mithilfe der Semaphore.SetAccessControl-Methode anwenden, allerdings bleibt so ein Verwundbarkeitszeitfenster zwischen der Erstellung und dem Schutz des Semaphors. Der Schutz von Semaphoren mit Zugriffssteuerungssicherheit hilft beim Verhindern böswilliger Angriffe, das Problem unbeabsichtigter Namenskonflikte wird dadurch aber nicht gelöst.

Siehe auch