ProgrammationDéveloppeur Backend

Quelles sont les variantes de synchronisation en Go ? Comment utiliser sync.Mutex, sync.RWMutex et sync.WaitGroup, et quelles sont les subtilités de chaque cas ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En Go, pour synchroniser les goroutines concurrentes, on utilise des structures du paquet sync, les plus courantes étant sync.Mutex, sync.RWMutex et sync.WaitGroup.

sync.Mutex fournit des mécanismes d'exclusion mutuelle lors de l'accès aux données partagées. Ses méthodes sont Lock() (bloque) et Unlock() (débloque).

sync.RWMutex étend les capacités d'un mutex classique : il permet une lecture parallèle, mais une modification exclusive.

sync.WaitGroup est destiné à attendre la fin d'un groupe de goroutines. À l'aide de Add(int), Done() et Wait(), vous gérez le cycle de vie du travail parallèle.

Par exemple :

var mu sync.RWMutex data := 0 // Lecture mu.RLock() fmt.Println(data) mu.RUnlock() // Écriture mu.Lock() data = 42 mu.Unlock()

Subtilités :

  • Utilisez toujours Unlock dans un bloc defer pour ne pas oublier de débloquer le mutex même en cas de panique.
  • Assurez-vous que le nombre d'appels à WaitGroup.Done() correspond à Add().
  • Ne copiez pas WaitGroup, Mutex et RWMutex !

Question piège

Peut-on acquérir le même sync.Mutex (ou RWMutex) deux fois de suite dans la même goroutine ? Que se passe-t-il ?

Réponse : Non, si vous appelez Lock() sur le même Mutex deux fois de suite sans Unlock() intermédiaire, la goroutine se bloquera indéfiniment (deadlock). En Go, les mutex ne sont pas récursifs.

Exemple :

var mu sync.Mutex mu.Lock() // ... mu.Lock() // DEADLOCK : se bloquera indéfiniment parce que le même thread détient déjà le verrou

Exemples d'erreurs réelles


Histoire

Dans un projet pour une API REST à fort trafic, un développeur a enveloppé tout le traitement de la requête avec un seul mutex. Cela a entraîné une forte chute des performances — une seule requête pouvait être traitée à la fois, alors que des milliers de clients étaient prévus. La raison était l'ignorance de la différence entre Mutex et RWMutex et l'ignorance des lectures parallèles.


Histoire

Lors de la copie d'une structure contenant un Mutex par un membre de l'équipe, une copie a été accidentellement transmise à une autre fonction. Cela a conduit à un message d'erreur "sync : copie de sync.Mutex" et des crashes en production sous forte charge.


Histoire

Lors de l'utilisation de WaitGroup, on a oublié d'appeler Done() dans plusieurs goroutines, ce qui a entraîné une attente infinie de Wait(), bloquant le thread principal. En conséquence, le service perdait sa disponibilité jusqu'à un redémarrage manuel.