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

Go
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: select se 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

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

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

Go
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

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

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

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

Implementați un select cu două cazuri care primesc de la ch1 și ch2; imprimați care a sosit primul.

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

Sintaxă `select { case ...: ... }` cu un caz pe canal.

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

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

Adăugați un caz de expirare la selectare folosind time.After(time.Second).

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

`time.After(d)` este un canal care primește după d.

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

Quiz#go.m7.l3.e3
Gata

Ce face ca un select să nu fie blocat?

Go
select { case v := <-ch: ... ??? }
Opțiuni de răspuns

Recapitulare

  • select aș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ți context).
  • Modele: canal terminat, buclă de evenimente, canal zero pentru a dezactiva un caz.
  • Gol select{} = bloc etern: aproape întotdeauna un bug.