Lecții din modul (4/5)
`sync.Mutex` și `sync.WaitGroup`
Canalele sunt limbajul principal al lui Go, dar uneori mai tradițional
sunt necesare primitive. Pachetul sync oferă mutexuri, așteptați
grupuri și alte utilități pentru a coordona starea comună.
sincronizare.Mutex: blocare exclusivă
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
}Între Lock() și Unlock() intră doar o o rutină odată.
Model obligatoriu: defer mu.Unlock() imediat după Lock().
sync.RWMutex: mulți cititori, un scriitor
Pentru starea de citire grea:
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()Util doar dacă citirile domină cu adevărat asupra scrierilor; altfel un obisnuit
sync.Mutex este mai simplă și adesea mai rapidă datorită internă mai mică
disputa.
sync.WaitGroup: așteptați N goroutine
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 lucru:
wg.Add(N)crește contorul cu N (înainte de lansarea goroutinelor).- Fiecare goroutine apelează
wg.Done()când se termină (ideal prindefer). wg.Wait()se blochează până când contorul revine la 0.
sincronizare.O dată: inițializare leneșă sigură pentru fire
var (
once sync.Once
config *Config
)
func GetConfig() *Config {
once.Do(func() {
config = loadConfig()
})
return config
}Reapelarea în once.Do rulează exact o dată, chiar și în condiții concomitente
apeluri. Model de inițializare singleton / leneș.
Mutex vs canal: când să folosești ce
Instrucțiuni (de asemenea, din secțiunea oficială Go FAQ):
| Caz | Instrument preferat |
|---|---|
| Trecerea dreptului de proprietate asupra datelor | canal |
| Distribuirea muncii (coadă de lucru) | canal |
| Coordonarea goroutinelor independente | canal |
| Protejarea unui câmp struct | Mutex |
| Cache / contor | Mutex sau atomic |
| Referință Singleton | sync.Once |
„Canale de orchestrat, mutexuri pentru date partajate” este o regulă generală bună.
Detector de curse
Rulați teste cu -race pentru a identifica cursele de date:
go test -race ./...
go run -race main.goDetectorul de curse instrumentează binarul și înregistrează fiecare nesincronizat acces la memoria partajată. Este instrumentul #1 pentru a valida codul concurent înainte de producție.
Încearcă
Protejați creșterea numărului cu mu.Lock() + defer mu.Unlock().
Afișează indiciu
defer mu.Unlock() imediat după Lock() garantează eliberarea chiar și în caz de panică.
Soluție disponibilă după 3 încercări
Lansați 3 goroutine și așteptați-le pe toate folosind sync.WaitGroup (Add(3), Done() în fiecare goroutine, Wait()).
Afișează indiciu
Adăugați ÎNAINTE de a lansa goroutine; Efectuat în interiorul goroutinei cu defer.
Soluție disponibilă după 3 încercări
Care este modelul recomandat pentru a garanta eliberarea mutex-ului?
mu.Lock()
// ?Recapitulare
sync.Mutexprotejează starea partajată; întotdeaunadefer mu.Unlock().- Utilizați un receptor pointer pentru a evita copierea mutex-ului.
sync.RWMutexpentru modele cu mai mulți cititori/un singur scriitor (doar dacă ajută).sync.WaitGroup:Addînainte dego,Donecudefer, __PROTECTED_INLINE_2 de așteptat.sync.Once: inițializare leneșă sigură pentru fire.- „Canale de orchestrat, mutexuri pentru date partajate”.
go test -race: instrumentul #1 pentru a identifica cursele de date.