Direct naar de hoofdinhoud
Module 7 · Les 3 van 533/50 in de cursus~12 min
Modulelessen (3/5)

`select`: kanalen multiplexen

select wacht op meerdere kanaalbewerkingen tegelijkertijd. Het is de switch van Go-concurrency: je kunt reageren op de eerste kanaal klaar voor velen.

Syntaxis

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")
}

Semantiek:

  • Alle cases worden geëvalueerd; slechts één van de kant-en-klare koffers wordt gekozen.
  • Indien meerdere koffers gereed zijn, is de keuze pseudo-willekeurig (geen bestelling).
  • Als er geen zaak klaar is: de select blokkeert totdat er één klaar is.
  • Als er een default is: deze wordt onmiddellijk uitgevoerd als er geen case gereed is (niet-blokkerende selectie).

Time-out patroon

Go
select {
case res := <-fetch(url):
    use(res)
case <-time.After(2 * time.Second):
    log.Println("timeout: fetch troppo lento")
}

time.After(d) retourneert een <-chan Time die een waarde oplevert na d. Het is de klassieke manier om het wachten te beperken.

Niet-blokkerende selectie met standaard

Go
select {
case msg := <-ch:
    process(msg)
default:
    // niente da fare, prosegui
}

Het "peilt" het kanaal zonder te blokkeren. Handig in codepaden die moet responsief blijven zonder verplicht te wachten.

Patroon 'Klaar kanaal'

Combineer select met een annuleringskanaal:

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) van de coördinator activeert case <-done in elke wachten goroutine: gecoördineerde uitschakeling.

Lus met selecteren

Go
for {
    select {
    case v := <-input:
        if v == nil {
            return
        }
        handle(v)
    case <-time.After(5 * time.Second):
        keepAlive()
    case <-ctx.Done():
        return
    }
}

Het typische 'event loop'-patroon voor servers en werkers.

Een nulkanaal als een "gehandicapt" geval

Een geavanceerde truc: een case <-ch met ch == nil is nooit geselecteerd. Hiermee kunt u een case dynamisch "uitschakelen":

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
    }
}

Wanneer in gesloten is, maak ik het ongeldig: select blijft alleen de andere gevallen.

Probeer het

Oefening#go.m7.l3.e1
Pogingen: 0Laden…

Implementeer een selectie met twee gevallen die ontvangen van ch1 en ch2; print welke als eerste arriveerde.

Editor laden…
Toon hint

Syntaxis `select { case ...: ... }` met één case per kanaal.

Oplossing beschikbaar na 3 pogingen

Oefening#go.m7.l3.e2
Pogingen: 0Laden…

Voeg een time-outgeval toe aan de selectie met behulp van time.After(time.Second).

Editor laden…
Toon hint

`time.After(d)` is een kanaal dat na d ontvangt.

Oplossing beschikbaar na 3 pogingen

Quiz#go.m7.l3.e3
Klaar

Wat maakt een selectie non-blocking?

Go
select { case v := <-ch: ... ??? }
Antwoordmogelijkheden

Samenvatten

  • select wacht op het eerste gereedstaande geval tussen verschillende kanaalbewerkingen.
  • Pseudo-willekeurige keuze uit meerdere kant-en-klare cases.
  • default → niet-blokkerende selectie (polling).
  • time.After(d) voor time-outs (let op het lek in lussen: gebruik context).
  • Patronen: voltooid kanaal, gebeurtenislus, nulkanaal om een ​​case uit te schakelen.
  • Leeg select{} = eeuwig blok: bijna altijd een bug.