Übersicht über Synchronisierungsprimitive

.NET bietet eine Reihe von Typen, mit denen Sie den Zugriff auf eine freigegebene Ressource synchronisieren oder die Threadinteraktion koordinieren können.

Wichtig

Verwenden Sie die gleiche Synchronisierungsprimitivinstanz, um den Zugriff einer freigegebenen Ressource zu schützen. Wenn Sie verschiedene Synchronisierungsprimitivinstanzen verwenden, um dieselbe Ressource zu schützen, umgehen Sie den Schutz, der von einem Synchronisierungsprimitive bereitgestellt wird.

WaitHandle-Klasse und einfache Synchronisierungstypen

Von der System.Threading.WaitHandle-Klasse werden mehrere .NET-Synchronisierungsprimitive abgeleitet, die ein natives Handle für die Betriebssystemsynchronisierung kapselt, und einen Signalmechanismus für die Threadinteraktion verwendet. Diese Klassen umfassen:

  • System.Threading.Mutex gewährt exklusiven Zugriff auf eine freigegebene Ressource. Der Zustand eines Mutex wird signalisiert, wenn er nicht in Besitz eines Threads ist.
  • System.Threading.Semaphore schränkt die Anzahl von Threads ein, die gleichzeitig auf eine freigegebene Ressource oder einen Pool von Ressourcen zugreifen können. Der Status eines Semaphors wird auf „signalisiert“ festgelegt, wenn die Anzahl größer als Null ist, bzw. wird auf „nicht signalisiert“ gesetzt, wenn die Anzahl Null ist.
  • System.Threading.EventWaitHandle stellt ein Threadsynchronisierungsereignis dar und kann sich entweder in einem signalisierten oder nicht signalisierten Zustand befinden.
  • System.Threading.AutoResetEvent wird aus EventWaitHandle abgeleitet und wird nach der Freigabe eines einzelnen wartenden Threads automatisch in einen nicht signalisierten Zustand zurückgesetzt, wenn einen Signalisierung eintritt.
  • System.Threading.ManualResetEvent wird aus EventWaitHandle abgeleitet bleibt bei einer Signalisierung in einem signalisierten Zustand, bis die Reset-Methode aufgerufen wird.

Da WaitHandle aus System.MarshalByRefObject abgeleitet wird, können diese Typen im .NET Framework verwendet werden, um die Aktivitäten von Threads über Anwendungsdomänengrenzen hinweg zu synchronisieren.

Im .NET Framework, in .NET Core und in .NET 5 oder höheren Versionen können einige dieser Typen benannte Systemsynchronisierungshandles darstellen, die im gesamten Betriebssystem sichtbar sind und die für die prozessübergreifende Synchronisierung verwendet werden können:

Weitere Informationen finden Sie in der Referenz für die WaitHandle-API.

Einfache Synchronisierungstypen basieren nicht auf zugrunde liegende Betriebssystemhandles und bieten in der Regel eine bessere Leistung. Allerdings können sie nicht für die prozessübergreifende Synchronisierung verwendet werden. Verwenden Sie diese Typen für die Threadsynchronisierung innerhalb einer Anwendung.

Einige dieser Typen stellen Alternativen zu den aus WaitHandle abgeleiteten Typen dar. Beispielsweise ist SemaphoreSlim eine einfache Alternative zu Semaphore.

Synchronisieren des Zugriffs auf eine freigegebene Ressource

.NET stellt eine Reihe von Synchronisierungsprimitiven zum Steuern des Zugriffs auf eine freigegebene Ressource von mehreren Threads bereit.

Monitor-Klasse

Die System.Threading.Monitor-Klasse gewährt den gegenseitig exklusiven Zugriff auf eine freigegebene Ressource, indem sie eine Sperre für das Objekt, das die Ressource identifiziert, abruft oder aufhebt. Während eine Sperre aufrechterhalten wird, kann der Thread, der die Sperre aufrechterhält, die Sperre abrufen und aufheben. Für jeden anderen Thread wird das Abrufen der Sperre blockiert, und die Monitor.Enter-Methode wartet auf die Aufhebung. Die Enter-Methode ruft eine freigegebene Sperre ab. Sie können die Monitor.TryEnter-Methode auch verwenden, um die Zeitspanne anzugeben, in der ein Thread versucht, eine Sperre abzurufen. Da die Monitor-Klasse Threadaffinität hat, muss der Thread, der eine Sperre erhalten hat, die Sperre durch Aufruf der Monitor.Exit-Methode freigeben.

Sie können die Interaktion von Threads, die eine Sperre für dasselbe Objekt erhalten, mit den Methoden Monitor.Wait, Monitor.Pulse und Monitor.PulseAll koordinieren.

Weitere Informationen finden Sie in der Referenz für die Monitor-API.

Hinweis

Verwenden Sie die lock-Anweisung in C# und die SyncLock-Anweisung in Visual Basic, um den Zugriff auf eine freigegebene Ressource zu synchronisieren, anstatt die Monitor-Klasse direkt zu verwenden. Die Anweisungen werden mithilfe der Enter- und Exit-Methoden implementiert und verwenden den try…finally-Block, um sicherzustellen, dass die erhaltene Sperre aufgehoben wird.

Mutex-Klasse

Die System.Threading.Mutex-Klasse gewährt wie Monitor exklusiven Zugriff auf eine freigegebene Ressource. Verwenden Sie eine der Mutex.WaitOne-Methodenüberladungen, um den Besitz eines Mutex anzufordern. Wie Monitor hat auch Mutex Threadaffinität und der Thread, der eine Mutex erworben hat, muss diesen durch den Aufruf der Mutex.ReleaseMutex-Methode freigeben.

Im Gegensatz zu Monitor kann die Mutex-Klasse für die prozessübergreifende Synchronisierung verwendet werden. Verwenden Sie dazu einen benannten Mutex, der im gesamten Betriebssystem sichtbar ist. Verwenden Sie zum Erstellen einer benannten Mutexinstanz einen Mutex-Konstruktor, der einen Namen angibt. Sie können auch die Mutex.OpenExisting-Methode aufrufen, um ein vorhandenes benanntes Systemmutex zu öffnen.

Weitere Informationen finden Sie im Artikel Mutexe und in der Referenz zur Mutex-API.

SpinLock-Struktur

Die System.Threading.SpinLock-Struktur gewährt wie Monitor exklusiven Zugriff auf eine freigegebene Ressource, basierend auf der Verfügbarkeit einer Sperre. Wenn SpinLock versucht, eine nicht verfügbare Sperre zu erhalten, wartet sie in einer Schleife und überprüft wiederholt, ob die Sperre verfügbar ist.

Weitere Informationen zu den Vor- und Nachteilen der Verwendung von SpinLock finden Sie im SpinLock-Artikel und in der Referenz zur SpinLock-API.

ReaderWriterLockSlim-Klasse

Die System.Threading.ReaderWriterLockSlim-Klasse gewährt exklusiven Schreibzugriff auf eine freigegebene Ressource und ermöglicht es mehreren Threads, gleichzeitig zum Lesen auf die Ressource zuzugreifen. Möglicherweise möchten Sie ReaderWriterLockSlim verwenden, um den Zugriff auf eine freigegebene Datenstruktur zu synchronisieren, die threadsichere Lesevorgänge unterstützt, aber exklusiven Zugriff für die Durchführung von Schreibvorgängen benötigt. Wenn ein Thread exklusiven Zugriff anfordert (z.B. durch Aufrufen der ReaderWriterLockSlim.EnterWriteLock-Methode), werden nachfolgende Writeranforderungen blockiert, bis alle vorhandenen Reader die Sperre beendet haben und der Writer die Sperre aktiviert und beendet hat.

Weitere Informationen finden Sie in der Referenz für die ReaderWriterLockSlim-API.

Semaphore- und SemaphoreSlim-Klassen

Die System.Threading.Semaphore- und System.Threading.SemaphoreSlim-Klassen schränken die Anzahl von Threads ein, die gleichzeitig auf eine freigegebene Ressource oder einen Pool von Ressourcen zugreifen können. Zusätzliche Threads, die die Ressource anfordern, warten, bis ein Thread das Semaphor freigibt. Da das Semaphor keine Threadaffinität hat, kann ein Thread das Semaphor erhalten und ein anderer kann es freigeben.

SemaphoreSlim ist eine einfache Alternative zu Semaphore und kann nur für die Synchronisierung innerhalb einer einzelnen Prozessgrenze verwendet werden.

Unter Windows können Sie Semaphore für die prozessübergreifende Synchronisierung verwenden. Erstellen Sie dafür eine Semaphore-Instanz, die ein benanntes Systemsemaphor darstellt, indem Sie einen der Semaphorskonstruktoren verwenden, die einen Namen oder die Semaphore.OpenExisting-Methode angeben. SemaphoreSlim unterstützt keine benannten Systemsemaphoren.

Weitere Informationen finden Sie im Artikel Semaphore und SemaphoreSlim und in der Referenz zur Semaphore- oder SemaphoreSlim-API.

Threadinteraktionen oder -signalisierung

Threadinteraktionen (oder Threadsignalisierung) bedeutet, dass ein Thread zum Fortsetzen des Vorgangs auf eine Benachrichtigung oder ein Signal von einem oder mehreren Threads warten muss. Wenn beispielsweise Thread A die Thread.Join-Methode von Thread B aufruft, wird Thread A blockiert, bis Thread B abgeschlossen ist. Die im vorhergehenden Abschnitt beschriebenen Synchronisierungsprimitive bieten einen anderen Mechanismus für die Signalisierung: Durch die Freigabe einer Sperre benachrichtigt ein Thread einen anderen Thread, dass er durch den Erhalt der Sperre fortgesetzt werden kann.

Dieser Abschnitt beschreibt zusätzliche Signalisierungskonstrukte, die von .NET bereitgestellt werden.

EventWaitHandle-, AutoResetEvent-, ManualResetEvent- und ManualResetEventSlim-Klassen

Die System.Threading.EventWaitHandle-Klasse stellt ein Threadsynchronisierungsereignis dar.

Ein Synchronisierungsereignis kann entweder in einem nicht signalisierten oder signalisierten Zustand sein. Wenn der Zustand eines Ereignisses nicht signalisiert ist, wird ein Thread, der die WaitOne-Überladung des Ereignisses aufruft, blockiert, bis ein Ereignis signalisiert wird. Die EventWaitHandle.Set Methode legt den Zustand eines Ereignisses auf „signalisiert“ fest.

Das Verhalten einer signalisierten EventWaitHandle hängt von den Zurücksetzmodus ab:

Unter Windows können Sie EventWaitHandle für die prozessübergreifende Synchronisierung verwenden. Erstellen Sie dafür eine EventWaitHandle-Instanz, die ein benanntes Systemsynchronisierungsereignis darstellt, indem Sie einen der EventWaitHandle-Konstruktoren, die einen Namen angeben, oder die EventWaitHandle.OpenExisting-Methode verwenden.

Weitere Informationen finden Sie im Artikel EventWaitHandle. Die API-Referenz finden Sie unter EventWaitHandle, AutoResetEvent, ManualResetEvent und ManualResetEventSlim.

CountdownEvent-Klasse

Die System.Threading.CountdownEvent-Klasse stellt ein Ereignis dar, das festgelegt wird, wenn die Anzahl null ist. Wenn CountdownEvent.CurrentCount größer als Null ist, wird ein Thread, der CountdownEvent.Wait abruft, blockiert. Rufen Sie CountdownEvent.Signal auf, um die Anzahl eines Ereignisses zu verringern.

Im Gegensatz zu ManualResetEvent oder ManualResetEventSlim, mit denen Sie mehrere Threads mit einem Signal von einem Thread entsperren können, können Sie mit CountdownEvent einen oder mehrere Threads mit Signalen von mehreren Threads entsperren.

Weitere Informationen finden Sie im Artikel CountdownEvent und in der Referenz zur CountdownEvent-API.

Barrier-Klasse

Die System.Threading.Barrier Klasse stellt eine Barriere zur Threadausführung dar. Ein Thread, der die Barrier.SignalAndWait-Methode aufruft, signalisiert, dass er die Barriere erreicht hat und wartet, bis andere teilnehmenden Threads die Barriere erreichen. Wenn alle teilnehmenden Threads die Barriere erreichen, werden sie weiter ausgeführt, und die Barriere wird zurückgesetzt und kann wieder verwendet werden.

Sie können Barrier verwenden, wenn ein oder mehrere Threads die Ergebnisse anderer Threads benötigen, bevor Sie mit der nächsten Berechnungsphase fortfahren.

Weitere Informationen finden Sie im Artikel Barriere und in der Referenz zur Barrier-API.

Interlocked-Klasse

Die System.Threading.Interlocked-Klasse stellt statische Methoden zur Verfügung, die einfache atomare Operationen an einer Variablen durchführen. Diese atomaren Operationen umfassen Addition, Inkrementieren und Dekrementieren, Austausch, bedingter Austausch je nach Vergleich und Lesevorgänge für einen 64-Bit-Ganzzahlenwert.

Weitere Informationen finden Sie in der Referenz für die Interlocked-API.

SpinWait-Struktur

Die System.Threading.SpinWait-Struktur stellt Unterstützung für Spin-basierte Wartevorgänge bereit. Sie können die Struktur verwenden, wenn ein Thread warten muss, bis ein Ereignis signalisiert oder eine Bedingung erfüllt wird, außer wenn die tatsächliche Wartezeit voraussichtlich kürzer als die erforderliche Wartezeit beim Verwenden eines Wait-Handles oder einer anderen Blockierung des Threads ist. Mithilfe von SpinWait können Sie einen kurzen Wartezeitraum angeben und danach nur dann auslösen (z. B. durch Warten oder Ruhezustand), wenn die Bedingung nicht in der angegebenen Zeit erfüllt wurde.

Weitere Informationen finden Sie im Artikel SpinWait und in der Referenz zur SpinWait-API.

Siehe auch