Ce face sync.Cond
sync.NewCond(&mu) creează o variabilă de condiție legată de un lacăt. sync.Cond leagă un lacăt (c.L, de obicei un *sync.Mutex) de un loc de așteptare. Un goroutine care are nevoie de o condiție (de exemplu „coada nu e goală”) ține lacătul și apelează c.Wait() într-o buclă for !conditie. Wait eliberează lacătul, doarme până primește semnal, apoi reia lacătul înainte să se întoarcă.
Cine schimbă starea apelează c.Signal() (trezește un singur goroutine care așteaptă) sau c.Broadcast() (îi trezește pe toți). Bucla for în jurul lui Wait este obligatorie: când un goroutine se trezește, condiția poate fi deja falsă din nou.
În cele mai multe programe Go, canalele (chan) sunt mai simple și mai greu de folosit greșit. Cond rămâne util când mulți așteaptă aceeași stare comună, protejată deja de un mutex.
Ce returnează
NewCond: *sync.Cond. Wait, Signal, Broadcast nu returnează nimic.
Exemple
Exemplul 1: Coadă cu Wait și Signal
Consumatorul așteaptă cât timp coada e goală. Producătorul adaugă o comandă și trezește consumatorul cu Signal.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var mu sync.Mutex
cond := sync.NewCond(&mu)
var coada []string
gata := make(chan bool)
go func() { // consumator
for range 3 {
mu.Lock()
for len(coada) == 0 {
cond.Wait()
}
comanda := coada[0]
coada = coada[1:]
mu.Unlock()
fmt.Println("Pregătesc", comanda)
}
gata <- true
}()
for _, c := range []string{"pizza", "ciorbă", "papanași"} {
time.Sleep(20 * time.Millisecond)
mu.Lock()
coada = append(coada, c)
mu.Unlock()
cond.Signal()
}
<-gata
}
Rezultat
Pregătesc pizza
Pregătesc ciorbă
Pregătesc papanași
Exemplul 2: Start comun pentru toți cu Broadcast
Cinci alergători așteaptă semnalul de start; Broadcast îi trezește pe toți deodată.
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
cond := sync.NewCond(&mu)
start := false
var pregatiti, porniti sync.WaitGroup
var plecati []int
for i := 1; i <= 5; i++ {
pregatiti.Add(1)
porniti.Add(1)
go func() {
defer porniti.Done()
mu.Lock()
pregatiti.Done()
for !start {
cond.Wait()
}
plecati = append(plecati, i)
mu.Unlock()
}()
}
pregatiti.Wait()
mu.Lock()
start = true
mu.Unlock()
fmt.Println("Start!")
cond.Broadcast()
porniti.Wait()
fmt.Println("Au plecat", len(plecati), "alergători")
}
Rezultat
Start!
Au plecat 5 alergători
Greșeli frecvente
- Apelezi
Waitfără să ții lacătul (c.L.Lock()): programul se oprește cu eroare. - Scrii
if !conditie { c.Wait() }în loc defor. După trezire condiția poate fi din nou falsă (alt goroutine a luat elementul). - Apelezi
Signalînainte ca cel care așteaptă să ajungă laWaitși nu ai o variabilă de stare: semnalul se pierde. Starea (start, coada) trebuie să existe separat de semnal.
Comenzi înrudite
sync.Mutex: Lacăt de excludere reciprocă: doar un goroutine pe rând poate rula codul dintre Lock() și Unlock().sync.WaitGroup: Așteaptă ca un grup de goroutine să termine: Add mărește contorul, Done îl scade, Wait blochează până ajunge la zero.context.Context: Interfața care duce prin program semnalul de anulare, termenul limită și valori legate de o cerere (ctx).