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
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 :
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
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 arrivatiFlux de travail :
wg.Add(N)incrémente le compteur de N (avant de lancer les goroutines).- Chaque goroutine appelle
wg.Done()à la fin (idéalement viadefer). wg.Wait()bloque jusqu'à ce que le compteur revienne à 0.
sync.Once : initialisation paresseuse thread-safe
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) :
| Cas | Outil préféré |
|---|---|
| Transmission de la propriété des données | canal |
| Répartition du travail (file d'attente des travaux) | canal |
| Coordonner des goroutines indépendantes | canal |
| Protéger un champ struct | Mutex |
| Cache/compteur | Mutex ou atomique |
| Référence singleton | sync.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 :
go test -race ./...
go run -race main.goLe 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
Protégez l'incrément de comptage avec mu.Lock() + defer mu.Unlock().
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
Lancez 3 goroutines et attendez toutes en utilisant sync.WaitGroup (Add(3), Done() dans chaque goroutine, Wait()).
Afficher l'indice
Ajoutez AVANT de lancer la goroutine ; Terminé À L'INTÉRIEUR de la goroutine avec report.
Solution disponible après 3 tentatives
Quel est le modèle recommandé pour garantir la libération du mutex ?
mu.Lock()
// ?Récapitulatif
sync.Mutexprotège l'état partagé ; toujoursdefer mu.Unlock().- Utilisez un récepteur pointeur pour éviter de copier le mutex.
sync.RWMutexpour les modèles à plusieurs lecteurs/un seul écrivain (uniquement si cela peut aider).sync.WaitGroup:Addavantgo,Doneavecdefer,Waitattendre.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.