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
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
selectblokkeert totdat er één klaar is. - Als er een
defaultis: deze wordt onmiddellijk uitgevoerd als er geen case gereed is (niet-blokkerende selectie).
Time-out patroon
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
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:
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
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":
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
Implementeer een selectie met twee gevallen die ontvangen van ch1 en ch2; print welke als eerste arriveerde.
Toon hint
Syntaxis `select { case ...: ... }` met één case per kanaal.
Oplossing beschikbaar na 3 pogingen
Voeg een time-outgeval toe aan de selectie met behulp van time.After(time.Second).
Toon hint
`time.After(d)` is een kanaal dat na d ontvangt.
Oplossing beschikbaar na 3 pogingen
Wat maakt een selectie non-blocking?
select { case v := <-ch: ... ??? }Samenvatten
selectwacht 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: gebruikcontext).- Patronen: voltooid kanaal, gebeurtenislus, nulkanaal om een case uit te schakelen.
- Leeg
select{}= eeuwig blok: bijna altijd een bug.