In Go worden structuren uit het sync-pakket gebruikt voor de synchronisatie van concurrerende goroutines, de meest voorkomende zijn sync.Mutex, sync.RWMutex en sync.WaitGroup.
sync.Mutex biedt mechanismen voor wederzijds uitsluiting bij toegang tot gedeelde gegevens. Zijn methoden zijn Lock() (vergrendelt) en Unlock() (ontgrendelt).
sync.RWMutex breidt de mogelijkheden van een gewone mutex uit: het staat gelijktijdig lezen toe, maar exclusieve wijziging.
sync.WaitGroup is bedoeld voor het wachten op de voltooiing van een groep goroutines. Met Add(int), Done() en Wait() beheert u de levenscyclus van parallel werk.
Bijvoorbeeld:
var mu sync.RWMutex data := 0 // Lezen mu.RLock() fmt.Println(data) mu.RUnlock() // Schrijven mu.Lock() data = 42 mu.Unlock()
Nuances:
Unlock in een defer-blok om te voorkomen dat u vergeet de mutex te ontgrendelen, zelfs bij paniek.WaitGroup.Done() overeenkomt met Add().Kan dezelfde sync.Mutex (of RWMutex) twee keer achter elkaar in dezelfde goroutine worden vastgelegd? Wat gebeurt er?
Antwoord: Nee, als u Lock() op dezelfde Mutex twee keer achter elkaar aanroept zonder tussenliggende Unlock(), zal de goroutine voor altijd vergrendeld zijn (deadlock). In Go zijn mutexen niet-recursief.
Voorbeeld:
var mu sync.Mutex mu.Lock() // ... mu.Lock() // DEADLOCK: zal voor altijd vergrendeld zijn, omdat dezelfde thread de lock al vasthoudt
Verhaal
In een project voor een high-load REST API wikkelde de ontwikkelaar de hele verwerking van de aanvraag in één mutex. Dit veroorzaakte een scherpe daling van de prestaties - slechts één aanvraag kon tegelijkertijd worden verwerkt, terwijl er duizenden klanten bedoeld waren te worden bediend. De oorzaak was onwetendheid over het verschil tussen Mutex en RWMutex en het negeren van gelijktijdig lezen.
Verhaal
Bij het kopiëren van een structuur met Mutex erin, gaf een van de teamleden per ongeluk een kopie door aan een andere functie. Dit leidde tot een paniekbericht "sync: copy of sync.Mutex" en crashes in producties onder hoge belasting.
Verhaal
Bij het gebruik van WaitGroup vergat men Done() in verschillende goroutines aan te roepen, wat leidde tot een eeuwige wachttijd op Wait(), waardoor de hoofdthread werd geblokkeerd. Hierdoor verloor de service de beschikbaarheid tot handmatige herstart.