Hoe worden mutexen geïmplementeerd?

Zijn sommige implementaties beter dan andere voor specifieke toepassingen? Valt er iets te verdienen door uw eigen uit te rollen?


Antwoord 1, autoriteit 100%

Bekijk de beschrijving van de Test-and-setmachine-instructie op Wikipedia, dat zinspeelt op hoe atomaire operaties worden bereikt op machineniveau. Ik kan me voorstellen dat de meeste mutex-implementaties op taalniveau afhankelijk zijn van ondersteuning op machineniveau, zoals Test-and-set.


Antwoord 2, autoriteit 83%

Voortbouwend op Adamski’s test-and-setsuggestie, zou je ook moeten kijken naar het concept van “snelle gebruikersruimte mutexen” of futexen.

Futexen hebben de gewenste eigenschap dat ze geen kernelsysteemaanroep nodig hebben in de veelvoorkomende gevallen van het vergrendelen of ontgrendelen van een onbevochtenmutex. In deze gevallen gebruikt de gebruikersmoduscode met succes een atomaire compare and swap (CAS)handeling om de mutex te vergrendelen of te ontgrendelen.

Als CAS faalt, wordt de mutex betwist en moet een kernelsysteemaanroep — sys_futexonder Linux — worden gebruikt om ofwel op de mutex te wachten (in de slotkast) of om andere threads te activeren (in de ontgrendeldoos).

Als je serieus bent om dit zelf te implementeren, lees dan ook Ulrich Drepper’s papier.


Antwoord 3, autoriteit 31%

Een mutexdraait bij voorkeur in de kernel van het besturingssysteem met behoud van de hoeveelheid code het zo kort mogelijk, zodat het kan voorkomen dat het wordt onderbroken tijdens het overschakelen naar een ander proces. De exacte uitvoering is daarom een ​​beetje geheim. Het is echter niet ingewikkeld. Het is eigenlijk een object met een booleaans veld, dat het krijgt en instelt.

  • Als je een teller gebruikt, kan deze een semafoor worden.
  • Een mutex is het startpunt voor een kritieke sectie, die intern een mutex gebruikt om te zien of deze een codesectie kan invoeren. Als de mutex vrij is, stelt het de mutex in en voert de code uit, alleen om de mutex vrij te geven als het klaar is. Wanneer een kritieke sectie merkt dat een mutex is vergrendeld, kan het wachten tot de mutex wordt vrijgegeven.

Rond de basis mutex-logica zijn er wrappers om het in een object te wikkelen. Dan meer wrapper-objecten om het buiten de kernel beschikbaar te maken. En dan nog een wrapper om het beschikbaar te maken in .NET. En dan zullen verschillende programmeurs hun eigen wrapper-code hier omheen schrijven, allemaal voor hun eigen logische behoeften. De wikkels rond wikkels maken ze echt een troebel gebied.

Nu, met deze basiskennis over de binnenkant van mutexen, hoop ik alleen dat je één implementatie gaat gebruiken die afhankelijk is van de kernel en de hardware eronder. Deze zouden het meest betrouwbaar zijn. (Als de hardware dit ondersteunt.) Als de mutex die je gebruikt niet werkt op dit kernel-/hardwareniveau, dan kan het nog steeds betrouwbaar zijn, maar ik zou adviseren om het niet te gebruiken, tenzij er geen alternatief is.

Voor zover ik weet, zullen Windows, Linux en .NET allemaal mutexen gebruiken op kernel-/hardwareniveau.

De Wikipedia-pagina waarnaar ik heb gelinkt, legt meer uit over de interne logica en mogelijke implementaties. Bij voorkeur wordt een mutex bestuurd door de hardware, waardoor het verkrijgen/instellen van de mutex een wordt. ondeelbare stap. (Om er zeker van te zijn dat het systeem niet tussendoor van taak wisselt.)


Antwoord 4, autoriteit 6%

Interlocked.CompareExchangeis voldoende om spinlocks te implementeren. Het is echter best moeilijk om het goed te doen. Zie voor Joe Duffy’s blogvoor een voorbeeld van de subtiliteiten.


Antwoord 5, autoriteit 6%

Een stukje montage om atomaire vergrendeling te demonstreren:

; BL is the mutex id
; shared_val, a memory address
CMP [shared_val],BL ; Perhaps it is locked to us anyway
JZ .OutLoop2
.Loop1:
CMP [shared_val],0xFF ; Free
JZ .OutLoop1 ; Yes
pause ; equal to rep nop.
JMP .Loop1 ; Else, retry
.OutLoop1:
; Lock is free, grab it
MOV AL,0xFF
LOCK CMPXCHG [shared_val],BL
JNZ .Loop1 ; Write failed
.OutLoop2: ; Lock Acquired

Antwoord 6

Ik heb Reflector.NET gebruikt om de broncode voor System.Threading.ReaderWriterLockSlimte decompileren, die is toegevoegd aan een recente versie van het .NET-framework.

Het gebruikt meestal Interlocked.CompareExchange, Thread.SpinWaiten Thread.Sleepom synchronisatie te bereiken. Er zijn enkele EventWaitHandle-instanties (kernelobject) die onder bepaalde omstandigheden worden gebruikt.

Er is ook wat complexiteit toegevoegd om herintreding op een enkele thread te ondersteunen.

Als je in dit gebied geïnteresseerd bent en in .NET werkt (of het in ieder geval kunt lezen), dan vind je het misschien best interessant om deze les eens te proberen.

Other episodes