In Go werden zur Synchronisation von konkurrierenden Goroutinen Strukturen aus dem Paket sync verwendet, die häufigsten sind sync.Mutex, sync.RWMutex und sync.WaitGroup.
sync.Mutex bietet Mechanismen zur wechselseitigen Exklusion beim Zugriff auf gemeinsame Daten. Die Methoden sind Lock() (sperrt) und Unlock() (entsperrt).
sync.RWMutex erweitert die Möglichkeiten eines normalen Mutex: paralleles Lesen ist erlaubt, aber exklusive Änderungen.
sync.WaitGroup ist gedacht, um das Ende einer Gruppe von Goroutinen abzuwarten. Mit Add(int), Done() und Wait() verwalten Sie den Lebenszyklus der parallelen Arbeit.
Beispiel:
var mu sync.RWMutex data := 0 // Lesen mu.RLock() fmt.Println(data) mu.RUnlock() // Schreiben mu.Lock() data = 42 mu.Unlock()
Feinheiten:
Unlock im defer-Block, um sicherzustellen, dass der Mutex auch im Falle einer Panik entsperrt wird.WaitGroup.Done() mit Add() übereinstimmt.Kann man denselben sync.Mutex (oder RWMutex) zweimal hintereinander in derselben Goroutine erfassen? Was passiert?
Antwort: Nein, wenn Sie Lock() auf demselben Mutex zweimal hintereinander ohne dazwischen liegendes Unlock() aufrufen, wird die Goroutine für immer blockiert (Deadlock). In Go sind Mutexe nicht rekursiv.
Beispiel:
var mu sync.Mutex mu.Lock() // ... mu.Lock() // DEADLOCK: wird für immer blockiert, da derselbe Thread bereits den Lock hält
Geschichte
In einem Projekt für eine hochbelastete REST-API hat ein Entwickler die gesamte Anfrageverarbeitung mit einem Mutex umschlossen. Dies führte zu einem drastischen Leistungsabfall – nur eine Anfrage konnte gleichzeitig bearbeitet werden, obwohl die Bedienung von tausenden von Kunden geplant war. Grund war das Unwissen über den Unterschied zwischen Mutex und RWMutex und die Ignorierung paralleler Lesevorgänge.
Geschichte
Bei der Kopie einer Struktur mit einem Mutex innerhalb eines Teammitglieds wurde versehentlich eine Kopie an eine andere Funktion übergeben. Dies führte zu einer Panikmeldung "sync: copy of sync.Mutex" und zu Abstürzen in der Produktion unter hoher Last.
Geschichte
Bei der Verwendung von WaitGroup wurde vergessen, Done() in mehreren Goroutinen aufzurufen, was zu einem ewigen Warten auf Wait() führte, das den Hauptthread blockierte. Infolgedessen verlor der Dienst bis zur manuellen Neugestaltung die Verfügbarkeit.