In Go zijn maps (map) standaard niet thread-safe. Als meerdere goroutines gelijktijdig naar dezelfde map schrijven of deze lezen zonder synchronisatie, ontstaat er een race condition (data race), wat leidt tot een panic met de boodschap fatal error: concurrent map read and map write of corruptie van gegevens.
Voor thread-safe toegang tot een map is het noodzakelijk:
sync.Map, dat bedoeld is voor hoogbelaste concurrerende scenario's.var mu sync.RWMutex m := make(map[string]int) // Schrijven mu.Lock() m["key"] = 1 mu.Unlock() // Lezen mu.RLock() v := m["key"] mu.RUnlock() // Of sync.Map var sm sync.Map sm.Store("key", 1) v, _ := sm.Load("key")
Waarom is gelijktijdig lezen en schrijven vanuit verschillende goroutines in een map zo gevaarlijk, ook al is map een ingebouwd type?
Antwoord: In Go biedt het ingebouwde type map geen synchronisatie. Gelijktijdige toegang tot een map vanuit meerdere goroutines resulteert niet alleen in onjuiste waarden, maar kan de applicatie ook laten crashen. Zelfs gelijktijdig lezen en schrijven (wanneer er geen overlappende sleutels zijn!) kan een fatale fout veroorzaken. Dit is anders dan in sommige andere talen, waar collecties 'tolerant' zijn voor gelijktijdige toegang.
Verhaal
In een echt project gebruikte een ontwikkelaar een globale map voor het cachen van gegevens. De service werkte stabiel in tests, maar bij belastingstests en in productie begon deze te crashen met de fout fatal error: concurrent map read and map write. De oorzaak was gelijktijdige toegang tot de map vanuit verschillende http-verzoeken zonder het gebruik van Mutex.
Verhaal
In een webapplicatie in Go besloot een programmeur de prestaties te verbeteren en gebruikte een gewone map als pool voor verbindingen, in de veronderstelling dat multi-threading door het framework werd gegarandeerd. Bij een plotselinge toename van het verkeer begon de service te crashen zonder duidelijke reden: door de race werden gegevens in de map beschadigd en ontstonden panieken.
Verhaal
In een interne Go-service gebruikte de applicatie een map om statistieken 'on the fly' te verzamelen, in de veronderstelling dat de hoofdtoegang alleen voor schrijven was. In werkelijkheid vroeg een andere deel van de code af en toe gegevens op voor rapportage, wat leidde tot moeilijk te detecteren crashes slechts één keer per dag - precies wanneer de statistieken werden uitgevoerd. Analyse toonde aan dat lezen en schrijven elkaar overlappen zonder enige blokkering.