Passer au contenu principal
eLearner.app
Module 7 · Leçon 4 sur 534/50 dans le cours~14 min
Leçons du module (4/5)

sync.Mutex et sync.WaitGroup

Les chaînes sont l'idiome principal de Go, mais parfois plus traditionnelles des primitives sont nécessaires. Le package sync fournit des mutex, attendez groupes et autres utilitaires pour coordonner l’état partagé.

sync.Mutex : verrouillage exclusif

Go
import "sync"

type Counter struct {
    mu    sync.Mutex
    count int
}

func (c *Counter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *Counter) Get() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

Une seule une goroutine à la fois entre entre Lock() et Unlock(). Modèle obligatoire : defer mu.Unlock() juste après Lock().

sync.RWMutex : plusieurs lecteurs, un seul écrivain

Pour un état lourd en lecture :

Go
var mu sync.RWMutex

mu.RLock()              // read lock: più goroutine simultanee OK
v := data
mu.RUnlock()

mu.Lock()               // write lock: esclusivo
data = newValue
mu.Unlock()

Utile uniquement si les lectures dominent vraiment les écritures ; sinon un habitué sync.Mutex est plus simple et souvent plus rapide grâce à une contestation.

sync.WaitGroup : attendez N goroutines

Go
var wg sync.WaitGroup

for i := 0; i < 3; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        work(id)
    }(i)
}
wg.Wait()    // blocca finché tutti i Done() sono arrivati

Flux de travail :

  1. wg.Add(N) incrémente le compteur de N (avant de lancer les goroutines).
  2. Chaque goroutine appelle wg.Done() à la fin (idéalement via defer).
  3. wg.Wait() bloque jusqu'à ce que le compteur revienne à 0.

sync.Once : initialisation paresseuse thread-safe

Go
var (
    once   sync.Once
    config *Config
)

func GetConfig() *Config {
    once.Do(func() {
        config = loadConfig()
    })
    return config
}

Le rappel dans once.Do s'exécute exactement une fois, même en mode simultané appels. Modèle d'initialisation singleton/paresseux.

Mutex vs canal : quand utiliser quoi

Lignes directrices (également tirées de la FAQ officielle de Go) :

CasOutil préféré
Transmission de la propriété des donnéescanal
Répartition du travail (file d'attente des travaux)canal
Coordonner des goroutines indépendantescanal
Protéger un champ structMutex
Cache/compteurMutex ou atomique
Référence singletonsync.Once

"Canaux à orchestrer, mutex pour les données partagées" est une bonne règle de base.

Détecteur de course

Exécutez des tests avec -race pour repérer les courses aux données :

Bash
go test -race ./...
go run -race main.go

Le détecteur de course instrumente le binaire et enregistre chaque non synchronisé accès à la mémoire partagée. C'est l'outil n°1 pour valider le code concurrent avant la production.

Essayez-le

Exercice#go.m7.l4.e1
Tentatives : 0Chargement…

Protégez l'incrément de comptage avec mu.Lock() + defer mu.Unlock().

Chargement de l'éditeur…
Afficher l'indice

différer mu.Unlock() juste après que Lock() garantit la libération même en cas de panique.

Solution disponible après 3 tentatives

Exercice#go.m7.l4.e2
Tentatives : 0Chargement…

Lancez 3 goroutines et attendez toutes en utilisant sync.WaitGroup (Add(3), Done() dans chaque goroutine, Wait()).

Chargement de l'éditeur…
Afficher l'indice

Ajoutez AVANT de lancer la goroutine ; Terminé À L'INTÉRIEUR de la goroutine avec report.

Solution disponible après 3 tentatives

Quiz#go.m7.l4.e3
Prêt

Quel est le modèle recommandé pour garantir la libération du mutex ?

Go
mu.Lock()
// ?
Options de réponse

Récapitulatif

  • sync.Mutex protège l'état partagé ; toujours defer mu.Unlock().
  • Utilisez un récepteur pointeur pour éviter de copier le mutex.
  • sync.RWMutex pour les modèles à plusieurs lecteurs/un seul écrivain (uniquement si cela peut aider).
  • sync.WaitGroup : Add avant go, Done avec defer, Wait attendre.
  • sync.Once : initialisation paresseuse thread-safe.
  • "Canaux à orchestrer, mutex pour données partagées".
  • defer mu.Unlock()0 : outil n°1 pour repérer les courses aux données.