Salt la conținutul principal
Modulul 7 · Lecția 4 din 534/50 în curs~14 min
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ă

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
}

Î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:

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()

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

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 lucru:

  1. wg.Add(N) crește contorul cu N (înainte de lansarea goroutinelor).
  2. Fiecare goroutine apelează wg.Done() când se termină (ideal prin defer).
  3. wg.Wait() se blochează până când contorul revine la 0.

sincronizare.O dată: inițializare leneșă sigură pentru fire

Go
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):

CazInstrument preferat
Trecerea dreptului de proprietate asupra datelorcanal
Distribuirea muncii (coadă de lucru)canal
Coordonarea goroutinelor independentecanal
Protejarea unui câmp structMutex
Cache / contorMutex sau atomic
Referință Singletonsync.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:

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

Detectorul 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ă

Exercițiu#go.m7.l4.e1
Încercări: 0Se încarcă…

Protejați creșterea numărului cu mu.Lock() + defer mu.Unlock().

Se încarcă editorul...
Afișează indiciu

defer mu.Unlock() imediat după Lock() garantează eliberarea chiar și în caz de panică.

Soluție disponibilă după 3 încercări

Exercițiu#go.m7.l4.e2
Încercări: 0Se încarcă…

Lansați 3 goroutine și așteptați-le pe toate folosind sync.WaitGroup (Add(3), Done() în fiecare goroutine, Wait()).

Se încarcă editorul...
Afișează indiciu

Adăugați ÎNAINTE de a lansa goroutine; Efectuat în interiorul goroutinei cu defer.

Soluție disponibilă după 3 încercări

Quiz#go.m7.l4.e3
Gata

Care este modelul recomandat pentru a garanta eliberarea mutex-ului?

Go
mu.Lock()
// ?
Opțiuni de răspuns

Recapitulare

  • sync.Mutex protejează starea partajată; întotdeauna defer mu.Unlock().
  • Utilizați un receptor pointer pentru a evita copierea mutex-ului.
  • sync.RWMutex pentru modele cu mai mulți cititori/un singur scriitor (doar dacă ajută).
  • sync.WaitGroup: Add înainte de go, Done cu defer, __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.