Lecții din modul (3/5)
`select`: multiplexarea canalelor
select așteaptă operațiuni pe mai multe canale în același timp.
Este concurența switch a Go: vă permite să reacționați la prima
canal gata printre multe.
Sintaxă
select {
case v := <-ch1:
fmt.Println("ch1:", v)
case v := <-ch2:
fmt.Println("ch2:", v)
case ch3 <- 99:
fmt.Println("inviato su ch3")
case <-time.After(time.Second):
fmt.Println("timeout")
default:
fmt.Println("nessuno è pronto")
}Semantică:
- Toate cazurile sunt evaluate; se alege doar unul dintre cazurile gata.
- Dacă sunt gata mai multe cazuri, alegerea este pseudo-aleatorie (fără comandă).
- Dacă niciun caz nu este gata:
selectse blochează până când unul devine gata. - Dacă există un
default: rulează imediat când niciun caz nu este pregătit (selectare neblocare).
Model de timeout
select {
case res := <-fetch(url):
use(res)
case <-time.After(2 * time.Second):
log.Println("timeout: fetch troppo lento")
}time.After(d) returnează un <-chan Time care produce o valoare după d.
Este modul clasic de a limita așteptarea.
Selectare fără blocare implicită
select {
case msg := <-ch:
process(msg)
default:
// niente da fare, prosegui
}„Sonda” canalul fără blocare. Util în căile de cod care trebuie să rămână receptiv fără a aștepta obligatoriu.
Model „Canal gata”.
Combinați select cu un canal de anulare:
func worker(in <-chan Job, done <-chan struct{}) {
for {
select {
case j := <-in:
process(j)
case <-done:
return // chi gestisce done segnala lo shutdown
}
}
}close(done) de la coordonator declanșează case <-done în fiecare
waiting goroutine: oprire coordonată.
Buclă cu select
for {
select {
case v := <-input:
if v == nil {
return
}
handle(v)
case <-time.After(5 * time.Second):
keepAlive()
case <-ctx.Done():
return
}
}Modelul tipic „buclă de evenimente” pentru servere și lucrători.
Un canal zero ca caz „dezactivat”.
Un truc avansat: un case <-ch cu ch == nil este niciodată
selectat. Vă permite să „dezactivați” un caz în mod dinamic:
var in chan int = source()
for {
select {
case v, ok := <-in:
if !ok {
in = nil // disabilita questo case
continue
}
handle(v)
case <-ctx.Done():
return
}
}Când in este închis, îl anulez: select continuă să gestioneze numai
alte cazuri.
Încearcă
Implementați un select cu două cazuri care primesc de la ch1 și ch2; imprimați care a sosit primul.
Afișează indiciu
Sintaxă `select { case ...: ... }` cu un caz pe canal.
Soluție disponibilă după 3 încercări
Adăugați un caz de expirare la selectare folosind time.After(time.Second).
Afișează indiciu
`time.After(d)` este un canal care primește după d.
Soluție disponibilă după 3 încercări
Ce face ca un select să nu fie blocat?
select { case v := <-ch: ... ??? }Recapitulare
selectașteaptă primul caz gata dintre mai multe operațiuni de canal.- Alegere pseudo-aleatorie între mai multe cazuri gata.
default→ selectare fără blocare (sondare).time.After(d)pentru expirări (atenție la scurgerea în bucle: utilizațicontext).- Modele: canal terminat, buclă de evenimente, canal zero pentru a dezactiva un caz.
- Gol
select{}= bloc etern: aproape întotdeauna un bug.