ProgrammierungBackend-Entwickler

Welche Arten von Synchronisation gibt es in Go? Wie verwendet man sync.Mutex, sync.RWMutex und sync.WaitGroup, und welche Feinheiten gibt es in jedem Fall?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

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:

  • Verwenden Sie immer Unlock im defer-Block, um sicherzustellen, dass der Mutex auch im Falle einer Panik entsperrt wird.
  • Stellen Sie sicher, dass die Anzahl der Aufrufe von WaitGroup.Done() mit Add() übereinstimmt.
  • Kopieren Sie WaitGroup, Mutex und RWMutex nicht!

Fangfrage

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

Beispiele realer Fehler


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.