Overzicht van synchronisatie primitiefen
.NET biedt verschillende typen die u kunt gebruiken om de toegang tot een gedeelde resource te synchroniseren of threadinteractie te coördineren.
Belangrijk
Gebruik hetzelfde primitieve exemplaar voor synchronisatie om de toegang van een gedeelde resource te beveiligen. Als u verschillende primitieve exemplaren van synchronisatie gebruikt om dezelfde resource te beveiligen, omzeilt u de beveiliging die wordt geboden door een synchronisatieprimitief.
WaitHandle-klasse en lichtgewicht synchronisatietypen
Meerdere .NET-synchronisatieprimitief zijn afgeleid van de System.Threading.WaitHandle klasse, die een systeemeigen synchronisatiegreep van het besturingssysteem inkapselt en een signaleringsmechanisme gebruikt voor threadinteractie. Deze klassen zijn onder andere:
- System.Threading.Mutex, die exclusieve toegang verleent tot een gedeelde resource. De status van een mutex wordt gesignaleerd als er geen thread eigenaar van is.
- System.Threading.Semaphore, waarmee het aantal threads wordt beperkt dat toegang heeft tot een gedeelde resource of een pool met resources tegelijk. De toestand van een semafore wordt ingesteld op gesignaleerd wanneer het aantal groter is dan nul en niet-ondertekend wanneer het aantal nul is.
- System.Threading.EventWaitHandle, dat een threadsynchronisatiegebeurtenis vertegenwoordigt en kan een gesignaleerde of niet-ondertekende status hebben.
- System.Threading.AutoResetEvent, die is afgeleid van EventWaitHandle en, wanneer gesignaleerd, wordt automatisch opnieuw ingesteld op een niet-ondertekende status nadat één wachtthread is vrijgegeven.
- System.Threading.ManualResetEvent, die is afgeleid van EventWaitHandle en, wanneer gesignaleerd, in een gesignaleerde toestand blijft totdat de Reset methode wordt aangeroepen.
In .NET Framework kunnen deze typen worden WaitHandleSystem.MarshalByRefObjectgebruikt om de activiteiten van threads over de grenzen van het toepassingsdomein te synchroniseren.
In .NET Framework, .NET Core en .NET 5+ kunnen sommige van deze typen benoemde systeemsynchronisatiegrepen vertegenwoordigen, die zichtbaar zijn in het hele besturingssysteem en kunnen worden gebruikt voor de synchronisatie tussen processen:
- Mutex
- Semaphore (in Windows)
- EventWaitHandle (in Windows)
Zie de WaitHandle API-verwijzing voor meer informatie.
Lichtgewicht synchronisatietypen zijn niet afhankelijk van onderliggende besturingsgrepen van het besturingssysteem en bieden doorgaans betere prestaties. Ze kunnen echter niet worden gebruikt voor de synchronisatie tussen processen. Gebruik deze typen voor threadsynchronisatie binnen één toepassing.
Sommige van deze typen zijn alternatieven voor de typen die zijn afgeleid van WaitHandle. Is bijvoorbeeld SemaphoreSlim een lichtgewicht alternatief voor Semaphore.
Synchronisatie van toegang tot een gedeelde resource
.NET biedt een reeks synchronisatieprimitief voor het beheren van de toegang tot een gedeelde resource door meerdere threads.
Klasse bewaken
De System.Threading.Monitor klasse verleent wederzijds exclusieve toegang tot een gedeelde resource door een vergrendeling op het object te verkrijgen of vrij te geven dat de resource identificeert. Terwijl een vergrendeling wordt vastgehouden, kan de thread die de vergrendeling bevat, de vergrendeling opnieuw verkrijgen en vrijgeven. Elke andere thread wordt geblokkeerd voor het verkrijgen van de vergrendeling en de Monitor.Enter methode wacht totdat de vergrendeling wordt vrijgegeven. De Enter methode verkrijgt een vrijgegeven vergrendeling. U kunt ook de Monitor.TryEnter methode gebruiken om de hoeveelheid tijd op te geven waarin een thread probeert een vergrendeling te verkrijgen. Omdat de Monitor klasse threadaffiniteit heeft, moet de thread die een vergrendeling heeft verkregen, de vergrendeling vrijgeven door de methode aan te Monitor.Exit roepen.
U kunt de interactie van threads coördineren die een vergrendeling op hetzelfde object verkrijgen met behulp van de Monitor.Wait, Monitor.Pulseen Monitor.PulseAll methoden.
Zie de Monitor API-verwijzing voor meer informatie.
Notitie
Gebruik de vergrendelingsinstructie in C# en de SyncLock-instructie in Visual Basic om de toegang tot een gedeelde resource te synchroniseren in plaats van de Monitor klasse rechtstreeks te gebruiken. Deze instructies worden geïmplementeerd met behulp van de Enter en Exit methoden en een try…finally
blok om ervoor te zorgen dat de verkregen vergrendeling altijd wordt vrijgegeven.
Mutex-klasse
De System.Threading.Mutex klasse Monitorverleent exclusieve toegang tot een gedeelde resource. Gebruik een van de Mutex.WaitOne-methode-overbelastingen om het eigendom van een mutex aan te vragen. Net zoals Monitor, Mutex heeft threadaffiniteit en de thread die een mutex heeft verkregen, moet deze vrijgeven door de Mutex.ReleaseMutex methode aan te roepen.
In tegenstelling tot Monitor, kan de Mutex klasse worden gebruikt voor synchronisatie tussen processen. Hiervoor gebruikt u een benoemde mutex, die zichtbaar is in het hele besturingssysteem. Als u een benoemd mutex-exemplaar wilt maken, gebruikt u een Mutex-constructor die een naam opgeeft. U kunt de Mutex.OpenExisting methode ook aanroepen om een bestaande benoemde systeemmutex te openen.
Zie het artikel Mutexes en de Mutex API-verwijzing voor meer informatie.
SpinLock-structuur
De System.Threading.SpinLock structuur Monitorverleent exclusieve toegang tot een gedeelde resource op basis van de beschikbaarheid van een vergrendeling. Wanneer SpinLock wordt geprobeerd een vergrendeling te verkrijgen die niet beschikbaar is, wacht deze in een lus en controleert deze herhaaldelijk totdat de vergrendeling beschikbaar is.
Zie het SpinLock-artikel en de SpinLock API-verwijzing voor meer informatie over de voordelen en nadelen van het gebruik van spin lock.
Klasse ReaderWriterLockSlim
De System.Threading.ReaderWriterLockSlim klasse verleent exclusieve toegang tot een gedeelde resource voor het schrijven en biedt meerdere threads tegelijkertijd toegang tot de resource voor lezen. Mogelijk wilt u de ReaderWriterLockSlim toegang synchroniseren met een gedeelde gegevensstructuur die threadveilige leesbewerkingen ondersteunt, maar exclusieve toegang vereist om schrijfbewerkingen uit te voeren. Wanneer een thread exclusieve toegang aanvraagt (bijvoorbeeld door de methode aan ReaderWriterLockSlim.EnterWriteLock te roepen), blokkeren volgende lezers- en schrijfaanvragen totdat alle bestaande lezers de vergrendeling hebben afgesloten en de schrijver de vergrendeling heeft ingevoerd en afgesloten.
Zie de ReaderWriterLockSlim API-verwijzing voor meer informatie.
Klassen Semaphore en SemaphoreSlim
De System.Threading.Semaphore en System.Threading.SemaphoreSlim klassen beperken het aantal threads dat gelijktijdig toegang heeft tot een gedeelde resource of een pool met resources. Aanvullende threads die de resource aanvragen, wachten totdat een thread de semaphore vrijgeeft. Omdat de semaphore geen threadaffiniteit heeft, kan een thread de semaphore verkrijgen en kan een andere thread deze vrijlaten.
SemaphoreSlim is een lichtgewicht alternatief voor Semaphore en kan alleen worden gebruikt voor synchronisatie binnen één procesgrens.
In Windows kunt u gebruiken Semaphore voor de synchronisatie tussen processen. Hiervoor maakt u een Semaphore exemplaar dat een benoemd systeemmafore vertegenwoordigt met behulp van een van de Semaphore-constructors die een naam of methode Semaphore.OpenExisting specificeert. SemaphoreSlim biedt geen ondersteuning voor benoemde systeemsemaforen.
Zie het artikel Semaphore en SemaphoreSlim en de Semaphore API-verwijzing SemaphoreSlim voor meer informatie.
Threadinteractie of signalering
Threadinteractie (of threadsignalering) betekent dat een thread moet wachten op melding, of een signaal, van een of meer threads om door te gaan. Als thread A bijvoorbeeld de Thread.Join methode van thread B aanroept, wordt thread A geblokkeerd totdat thread B is voltooid. De synchronisatieprimitief die in de vorige sectie wordt beschreven, bieden een ander mechanisme voor signalering: door een vergrendeling vrij te geven, meldt een thread een andere thread die kan worden voortgezet door de vergrendeling te verkrijgen.
In deze sectie worden aanvullende signaleringsconstructies beschreven die worden geleverd door .NET.
De klassen EventWaitHandle, AutoResetEvent, ManualResetEvent en ManualResetEventSlim
De System.Threading.EventWaitHandle klasse vertegenwoordigt een threadsynchronisatie-gebeurtenis.
Een synchronisatiegebeurtenis kan een niet-ondertekende of gesignaleerde status hebben. Wanneer de status van een gebeurtenis niet is ondertekend, wordt een thread die de overbelasting van de gebeurtenis WaitOne aanroept, geblokkeerd totdat een gebeurtenis wordt gesignaleerd. Met EventWaitHandle.Set de methode wordt de status van een gebeurtenis ingesteld op signalering.
Het gedrag van een EventWaitHandle gesignaleerde is afhankelijk van de resetmodus:
- Een EventWaitHandle gemaakt met de EventResetMode.AutoReset vlag wordt automatisch opnieuw ingesteld na het vrijgeven van één wachtende thread. Het is net een draaiing die slechts één thread doorgeeft telkens wanneer het wordt gesignaleerd. De System.Threading.AutoResetEvent klasse, die is afgeleid van EventWaitHandle, vertegenwoordigt dat gedrag.
- Een EventWaitHandle gemaakt met de EventResetMode.ManualReset vlag blijft gesignaleerd totdat de Reset methode wordt aangeroepen. Het is net een poort die gesloten is totdat gesignaleerd wordt en vervolgens open blijft totdat iemand het sluit. De System.Threading.ManualResetEvent klasse, die is afgeleid van EventWaitHandle, vertegenwoordigt dat gedrag. De System.Threading.ManualResetEventSlim klasse is een lichtgewicht alternatief voor ManualResetEvent.
In Windows kunt u gebruiken EventWaitHandle voor de synchronisatie tussen processen. Hiervoor maakt u een EventWaitHandle exemplaar dat een benoemde systeemsynchronisatie-gebeurtenis vertegenwoordigt met behulp van een van de EventWaitHandle-constructors die een naam of methode EventWaitHandle.OpenExisting opgeven.
Zie het artikel EventWaitHandle voor meer informatie. Zie , AutoResetEvent, en ManualResetEventSlimManualResetEventvoor de API-verwijzingEventWaitHandle.
CountdownEvent-klasse
De System.Threading.CountdownEvent klasse vertegenwoordigt een gebeurtenis die wordt ingesteld wanneer het aantal nul is. Hoewel CountdownEvent.CurrentCount deze groter is dan nul, wordt een thread die wordt aanroepen CountdownEvent.Wait geblokkeerd. Aanroep CountdownEvent.Signal om het aantal gebeurtenissen te verlagen.
In tegenstelling tot ManualResetEvent of ManualResetEventSlim, waarmee u meerdere threads kunt deblokkeren met een signaal van één thread, kunt CountdownEvent u een of meer threads met signalen van meerdere threads deblokkeren.
Zie het artikel CountdownEvent en de CountdownEvent API-verwijzing voor meer informatie.
Barrièreklasse
De System.Threading.Barrier klasse vertegenwoordigt een threaduitvoeringsbarrière. Een thread die de Barrier.SignalAndWait methode aanroept, geeft aan dat de barrière is bereikt en wacht totdat andere deelnemersthreads de barrière bereiken. Wanneer alle deelnemersthreads de barrière bereiken, gaan ze verder en wordt de barrière opnieuw ingesteld en kan deze opnieuw worden gebruikt.
U kunt gebruiken Barrier wanneer een of meer threads de resultaten van andere threads vereisen voordat u verdergaat met de volgende berekeningsfase.
Zie het artikel Barrier en de Barrier API-verwijzing voor meer informatie.
Ingesloten klasse
De System.Threading.Interlocked klasse biedt statische methoden waarmee eenvoudige atomische bewerkingen op een variabele worden uitgevoerd. Deze atomische bewerkingen omvatten optellen, verhogen en verlagen, uitwisselen en voorwaardelijke uitwisseling die afhankelijk is van een vergelijking en leesbewerking van een 64-bits geheel getal.
Zie de Interlocked API-verwijzing voor meer informatie.
SpinWait-structuur
De System.Threading.SpinWait structuur biedt ondersteuning voor spin-based waiting. Mogelijk wilt u deze gebruiken wanneer een thread moet wachten tot een gebeurtenis moet worden gesignaleerd of aan een voorwaarde moet worden voldaan, maar wanneer de werkelijke wachttijd naar verwachting minder is dan de wachttijd die nodig is met behulp van een wachtgreep of door de thread op een andere manier te blokkeren. Met behulp SpinWaitvan , kunt u een korte periode opgeven om te draaien tijdens het wachten, en vervolgens rendement (bijvoorbeeld door te wachten of slapen) alleen als aan de voorwaarde niet in de opgegeven tijd is voldaan.
Zie het SpinWait-artikel en de SpinWait API-verwijzing voor meer informatie.