Ce face select
select seamănă cu un switch, dar fiecare case este o trimitere sau o primire pe un canal. select așteaptă până când una dintre operații poate avea loc și o execută doar pe aceea. Dacă mai multe sunt gata în același timp, alege una la întâmplare.
Cu o ramură default, select nu mai așteaptă: dacă niciun canal nu este gata, execută default imediat (trimitere sau primire „fără blocare”). Cu time.After sau ctx.Done() obții timeout și anulare. Un select {} gol blochează pentru totdeauna.
Un canal nil nu este niciodată gata, deci o ramură cu un canal setat la nil este practic dezactivată: așa se oprește ascultarea unui canal deja închis.
Exemple
Exemplul 1: Timeout cu time.After
Primul răspuns vine după 5 ms, deci sosește; al doilea ar veni după 2 secunde, dar timeout-ul este de 300 ms.
package main
import (
"fmt"
"time"
)
func cerere(durata time.Duration) <-chan string {
c := make(chan string, 1)
go func() {
time.Sleep(durata)
c <- fmt.Sprintf("răspuns după %v", durata)
}()
return c
}
func main() {
for _, d := range []time.Duration{5 * time.Millisecond, 2 * time.Second} {
select {
case r := <-cerere(d):
fmt.Println(r)
case <-time.After(300 * time.Millisecond):
fmt.Println("timeout: serverul nu a răspuns în 300ms")
}
}
}
Rezultat
răspuns după 5ms
timeout: serverul nu a răspuns în 300ms
Exemplul 2: Trimitere fără blocare cu default
Coada are loc pentru 2 mesaje; al treilea este respins în loc să blocheze programul.
package main
import "fmt"
func main() {
notificari := make(chan string, 2)
for _, m := range []string{"mesaj nou", "comandă livrată", "reducere 20%"} {
select {
case notificari <- m:
fmt.Println("pus în coadă:", m)
default:
fmt.Println("coada e plină, ignor:", m)
}
}
select {
case m := <-notificari:
fmt.Println("primul din coadă:", m)
default:
fmt.Println("nimic de citit")
}
}
Rezultat
pus în coadă: mesaj nou
pus în coadă: comandă livrată
coada e plină, ignor: reducere 20%
primul din coadă: mesaj nou
Exemplul 3: Citire din două canale până se închid amândouă
Canalul închis este setat la nil, ca select să nu-l mai aleagă. Totalurile nu depind de ordinea sosirii.
package main
import "fmt"
func trimite(valori ...int) <-chan int {
c := make(chan int)
go func() {
defer close(c)
for _, v := range valori {
c <- v
}
}()
return c
}
func main() {
iasi := trimite(120, 80, 45)
cluj := trimite(200, 15)
totalIasi, totalCluj := 0, 0
for iasi != nil || cluj != nil {
select {
case v, ok := <-iasi:
if !ok {
iasi = nil
continue
}
totalIasi += v
case v, ok := <-cluj:
if !ok {
cluj = nil
continue
}
totalCluj += v
}
}
fmt.Println("vânzări Iași:", totalIasi, "lei")
fmt.Println("vânzări Cluj:", totalCluj, "lei")
}
Rezultat
vânzări Iași: 245 lei
vânzări Cluj: 215 lei
Greșeli frecvente
- Așteptarea ca
selectsă aleagă primulcasedin cod când mai multe sunt gata: alegerea este aleatorie. defaultîntr-o buclăfor { select { ... default: } }fără pauză: bucla consumă 100% CPU.- Citirea în continuare dintr-un canal închis: primești la nesfârșit valoarea zero. Verifică
okși setează canalul lanil. time.Aftercreat la fiecare iterație într-o buclă rapidă: creează multe timere; pentru bucle lungi folosește untime.Timersau un context.
Comenzi înrudite
chan și canale (channels): Canalele (chan) transmit valori între goroutine în siguranță și le sincronizează; se creează cu make.go și goroutine: Cuvântul go pornește o funcție într-o goroutine: un fir de execuție ușor, care rulează în paralel cu restul programului.switch, case și default: Alege una din mai multe ramuri după valoarea unei expresii sau după prima condiție adevărată.close: Închide un canal: anunță că nu se mai trimit valori; range se oprește, iar primirea dă ok == false.