Lekcje modułu (3/5)
`select`: multipleksowanie kanałów
select oczekuje na operacje na wielu kanałach jednocześnie.
Jest to switch współbieżności Go: pozwala reagować na pierwszy
kanał gotowy wśród wielu.
Składnia
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")
}Semantyka:
- Wszystkie przypadki są oceniane; wybierany jest tylko jeden z gotowych przypadków.
- Jeśli gotowych jest wiele spraw, wybór jest pseudolosowy (bez kolejności).
- Jeśli żaden przypadek nie jest gotowy:
selectblokuje się, dopóki jeden nie będzie gotowy. - Jeśli istnieje
default: działa natychmiast, gdy żadna skrzynka nie jest gotowa (wybór nieblokujący).
Wzór przekroczenia limitu czasu
select {
case res := <-fetch(url):
use(res)
case <-time.After(2 * time.Second):
log.Println("timeout: fetch troppo lento")
}time.After(d) zwraca <-chan Time, który generuje wartość po d.
Jest to klasyczny sposób ograniczenia czasu oczekiwania.
Wybór nieblokujący z ustawieniem domyślnym
select {
case msg := <-ch:
process(msg)
default:
// niente da fare, prosegui
}„Odpytuje” kanał bez blokowania. Przydatne w ścieżkach kodu, które musi reagować bez konieczności czekania.
Wzór „Kanał gotowy”.
Połącz select z kanałem anulowania:
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) od koordynatora wyzwala case <-done w każdym przypadku
oczekiwanie na goroutine: skoordynowane wyłączenie.
Zapętl z zaznaczeniem
for {
select {
case v := <-input:
if v == nil {
return
}
handle(v)
case <-time.After(5 * time.Second):
keepAlive()
case <-ctx.Done():
return
}
}Typowy wzorzec „pętli zdarzeń” dla serwerów i procesów roboczych.
Kanał zerowy jako przypadek „wyłączony”.
Zaawansowana sztuczka: case <-ch z ch == nil to nigdy
wybrane. Umożliwia dynamiczne „wyłączanie” sprawy:
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
}
}Kiedy in jest zamknięty, zeruję go: select nadal obsługuje tylko
inne przypadki.
Spróbuj
Zaimplementuj wybór z dwoma przypadkami, które odbierają z kanałów 1 i ch2; wydrukuj, który przybył pierwszy.
Pokaż wskazówkę
Składnia `select { case ...: ... }` z jednym przypadkiem na kanał.
Rozwiązanie dostępne po 3 próbach
Dodaj przypadek przekroczenia limitu czasu do wyboru za pomocą time.After (time.Second).
Pokaż wskazówkę
`time.After(d)` to kanał odbierający po d.
Rozwiązanie dostępne po 3 próbach
Co sprawia, że wybór nie blokuje?
select { case v := <-ch: ... ??? }Podsumowanie
selectczeka na pierwszy gotowy przypadek spośród kilku operacji na kanale.- Wybór pseudolosowy spośród wielu gotowych przypadków.
default→ wybór nieblokujący (odpytywanie).time.After(d)dla przekroczeń limitu czasu (pamiętaj o wyciekach w pętlach: użyjcontext).- Wzorce: kanał gotowy, pętla zdarzeń, kanał zerowy do wyłączenia przypadku.
- Pusty
select{}= wieczny blok: prawie zawsze błąd.