Przejdź do głównej treści
eLearner.app
Moduł 7 · Lekcja 3 z 533/50 w kursie~12 min
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

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

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: select blokuje 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

Go
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

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

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) od koordynatora wyzwala case <-done w każdym przypadku oczekiwanie na goroutine: skoordynowane wyłączenie.

Zapętl z zaznaczeniem

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

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

Kiedy in jest zamknięty, zeruję go: select nadal obsługuje tylko inne przypadki.

Spróbuj

Ćwiczenie#go.m7.l3.e1
Próby: 0Ładowanie...

Zaimplementuj wybór z dwoma przypadkami, które odbierają z kanałów 1 i ch2; wydrukuj, który przybył pierwszy.

Ładowanie edytora...
Pokaż wskazówkę

Składnia `select { case ...: ... }` z jednym przypadkiem na kanał.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#go.m7.l3.e2
Próby: 0Ładowanie...

Dodaj przypadek przekroczenia limitu czasu do wyboru za pomocą time.After (time.Second).

Ładowanie edytora...
Pokaż wskazówkę

`time.After(d)` to kanał odbierający po d.

Rozwiązanie dostępne po 3 próbach

Quiz#go.m7.l3.e3
Gotowe

Co sprawia, że ​​wybór nie blokuje?

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

Podsumowanie

  • select czeka 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żyj context).
  • Wzorce: kanał gotowy, pętla zdarzeń, kanał zerowy do wyłączenia przypadku.
  • Pusty select{} = wieczny blok: prawie zawsze błąd.