programowanieProgramista Backend

Jakie są rodzaje synchronizacji w Go? Jak używać sync.Mutex, sync.RWMutex i sync.WaitGroup oraz jakie są szczegóły dotyczące każdego z tych przypadków?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

W Go do synchronizacji konkurencyjnych gorutin używa się struktur z pakietu sync, najczęściej są to sync.Mutex, sync.RWMutex i sync.WaitGroup.

sync.Mutex zapewnia mechanizmy wzajemnego wykluczenia przy dostępie do współdzielonych danych. Jego metody to Lock() (blokuje) i Unlock() (odblokowuje).

sync.RWMutex rozszerza możliwości zwykłego mutexa: pozwala na równoległe czytanie, ale wyłączną zmianę.

sync.WaitGroup służy do oczekiwania na zakończenie grupy gorutin. Używając Add(int), Done() i Wait(), zarządzasz cyklem życia pracy równoległej.

Na przykład:

var mu sync.RWMutex data := 0 // Czytanie mu.RLock() fmt.Println(data) mu.RUnlock() // Zapis mu.Lock() data = 42 mu.Unlock()

Szczegóły:

  • Zawsze używaj Unlock w bloku defer, aby nie zapomnieć odblokować mutexa nawet w przypadku paniki.
  • Upewnij się, że liczba wywołań WaitGroup.Done() odpowiada Add().
  • Nie kopiuj WaitGroup, Mutex i RWMutex!

Pytanie z pułapką

Czy można zablokować ten sam sync.Mutex (lub RWMutex) dwa razy pod rząd w tej samej gorutinie? Co się wydarzy?

Odpowiedź: Nie, jeśli wywołasz Lock() na tym samym Mutex dwa razy pod rząd bez pośredniego Unlock(), gorutyna zablokuje się na zawsze (deadlock). W Go mutexy nie są rekurencyjne.

Przykład:

var mu sync.Mutex mu.Lock() // ... mu.Lock() // DEADLOCK: zablokujemy się na zawsze, ponieważ ten sam wątek już trzyma blokadę

Przykłady rzeczywistych błędów


Historia

W projekcie dla high-load REST API programista owinął całe przetwarzanie żądania jednym mutexem. Spowodowało to gwałtowny spadek wydajności — tylko jedno żądanie mogło być przetwarzane jednocześnie, chociaż planowane było obsługiwanie tysięcy klientów. Powód — brak wiedzy na temat różnicy między Mutex a RWMutex oraz ignorowanie równoległych odczytów.


Historia

Podczas kopiowania struktury z Mutex w środku jeden z członków zespołu przypadkowo przekazał kopię do innej funkcji. Doprowadziło to do panicznego komunikatu "sync: copy of sync.Mutex" i awarii w produkcji pod wysokim obciążeniem.


Historia

Podczas używania WaitGroup zapomniano wywołać Done() w kilku gorutinach, co doprowadziło do wiecznego oczekiwania Wait(), blokującego główny wątek. W rezultacie usług nie można było używać do ręcznego ponownego uruchomienia.