Modulelessen (4/5)
`sync.Mutex` en `sync.WaitGroup`
Kanalen zijn het primaire idioom van Go, maar soms traditioneler
primitieven zijn nodig. Het sync pakket biedt mutexen, wacht
groepen en andere hulpprogramma's om de gedeelde staat te coördineren.
sync.Mutex: exclusief slot
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
}Slechts één goroutine tegelijk komt binnen tussen Lock() en Unlock().
Verplicht patroon: defer mu.Unlock() direct na Lock().
sync.RWMutex: veel lezers, één schrijver
Voor een leeszware status:
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()Alleen nuttig als leesbewerkingen echt domineren over schrijfbewerkingen; anders een normale
sync.Mutex is eenvoudiger en vaak sneller dankzij de lagere interne gegevens
stelling.
sync.WaitGroup: wacht op 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 arrivatiWerkstroom:
wg.Add(N)verhoogt de teller met N (voordat de goroutines worden gestart).- Elke goroutine roept
wg.Done()aan als deze klaar is (idealiter viadefer). wg.Wait()blokkeert totdat de teller teruggaat naar 0.
sync.Once: thread-safe luie initialisatie
var (
once sync.Once
config *Config
)
func GetConfig() *Config {
once.Do(func() {
config = loadConfig()
})
return config
}De callback in once.Do wordt precies één keer uitgevoerd, zelfs onder gelijktijdig gebruik
oproepen. Singleton / lui initialisatiepatroon.
Mutex versus kanaal: wanneer wat te gebruiken
Richtlijnen (ook uit de officiële Go FAQ):
| Geval | Voorkeurstool |
|---|---|
| Eigendom van gegevens doorgeven | kanaal |
| Werk verdelen (werkwachtrij) | kanaal |
| Coördineren van onafhankelijke goroutines | kanaal |
| Een struct-veld beveiligen | Mutex |
| Cache / teller | Mutex of atomair |
| Singleton-referentie | sync.Once |
"Kanalen om te orkestreren, mutexen voor gedeelde gegevens" is een goede vuistregel.
Rasdetector
Voer tests uit met -race om dataraces te spotten:
go test -race ./...
go run -race main.goDe racedetector meet het binaire getal en registreert elke niet-gesynchroniseerde toegang tot gedeeld geheugen. Het is tool #1 om gelijktijdige code te valideren vóór productie.
Probeer het
Bescherm de toename van het aantal met mu.Lock() + stel mu.Unlock() uit.
Toon hint
stel mu.Unlock() uit direct nadat Lock() vrijgave garandeert, zelfs bij paniek.
Oplossing beschikbaar na 3 pogingen
Start 3 goroutines en wacht op ze allemaal met behulp van sync.WaitGroup (Add(3), Done() in elke goroutine, Wait()).
Toon hint
Voeg toe VOORDAT u de goroutine start; Gedaan BINNEN de goroutine met uitstel.
Oplossing beschikbaar na 3 pogingen
Wat is het aanbevolen patroon om te garanderen dat de mutex wordt vrijgegeven?
mu.Lock()
// ?Samenvatten
sync.Mutexbeschermt de gedeelde status; altijddefer mu.Unlock().- Gebruik een pointer-ontvanger om te voorkomen dat de mutex wordt gekopieerd.
sync.RWMutexvoor patronen met veel lezers/één schrijver (alleen als dit helpt).sync.WaitGroup:Addvóórgo,Donemetdefer,Waitom te wachten.sync.Once: thread-safe luie initialisatie.- "Kanalen om te orkestreren, mutexen voor gedeelde gegevens".
go test -race: tool #1 om dataraces te spotten.