Lektionen des Moduls (3/5)
`select`: Multiplexing von Kanälen
select wartet auf mehrere Kanaloperationen gleichzeitig.
Es ist der switch der Go-Parallelität: Er ermöglicht es Ihnen, auf den ersten zu reagieren
Kanal bereit unter vielen.
Syntax
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")
}Semantik:
- Alle Fälle werden ausgewertet; Nur einer der fertigen Fälle wird ausgewählt.
- Wenn mehrere Fälle bereit sind, erfolgt die Auswahl pseudozufällig (keine Reihenfolge).
- Wenn kein Fall bereit ist: Der
selectblockiert, bis einer bereit wird. - Wenn ein
defaultvorhanden ist: Dieser wird sofort ausgeführt, wenn kein Fall bereit ist (nicht blockierende Auswahl).
Timeout-Muster
select {
case res := <-fetch(url):
use(res)
case <-time.After(2 * time.Second):
log.Println("timeout: fetch troppo lento")
}time.After(d) gibt einen <-chan Time zurück, der einen Wert nach d erzeugt.
Es ist die klassische Art, die Wartezeit zu verkürzen.
Nicht blockierende Auswahl mit Standardeinstellung
select {
case msg := <-ch:
process(msg)
default:
// niente da fare, prosegui
}Es „fragt“ den Kanal ab, ohne ihn zu blockieren. Nützlich in Codepfaden, die muss reaktionsfähig bleiben, ohne zwingend warten zu müssen.
Muster „Fertig-Kanal“.
Kombinieren Sie select mit einem Stornierungskanal:
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) vom Koordinator löst jeweils case <-done aus
Wartende Goroutine: Koordiniertes Herunterfahren.
Schleife mit Auswahl
for {
select {
case v := <-input:
if v == nil {
return
}
handle(v)
case <-time.After(5 * time.Second):
keepAlive()
case <-ctx.Done():
return
}
}Das typische „Ereignisschleifen“-Muster für Server und Worker.
Ein Null-Kanal als „deaktivierter“ Fall
Ein fortgeschrittener Trick: Ein case <-ch mit ch == nil ist nie
ausgewählt. Damit können Sie einen Fall dynamisch „deaktivieren“:
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
}
}Wenn in geschlossen ist, setze ich es auf Null: select verarbeitet weiterhin nur das
andere Fälle.
Probieren Sie es aus
Implementieren Sie eine Auswahl mit zwei Fällen, die von CH1 und CH2 empfangen. Drucken Sie aus, welches zuerst angekommen ist.
Hinweis anzeigen
Syntax `select { case ...: ... }` mit einem Fall pro Kanal.
Lösung nach 3 Versuchen verfügbar
Fügen Sie der Auswahl mithilfe von time.After(time.Second) einen Timeout-Fall hinzu.
Hinweis anzeigen
`time.After(d)` ist ein Kanal, der nach d empfängt.
Lösung nach 3 Versuchen verfügbar
Was macht eine Auswahl nicht blockierend?
select { case v := <-ch: ... ??? }Zusammenfassung
– select wartet auf den ersten Bereitschaftsfall unter mehreren Kanaloperationen.
- Pseudozufällige Auswahl aus mehreren fertigen Fällen.
default→ nicht blockierende Auswahl (Polling).time.After(d)für Zeitüberschreitungen (beachten Sie das Leck in Schleifen: verwenden Siecontext).- Muster: Fertig-Kanal, Ereignisschleife, Null-Kanal zum Deaktivieren eines Falls.
- Leerer
select{}= ewiger Block: fast immer ein Fehler.