Модульні уроки (3/5)
`select`: мультиплексування каналів
select очікує на кілька канальних операцій одночасно.
Це switch паралелізму 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")
}Семантика:
- Всі справи оцінюються; Вибирається тільки один із готових кейсів.
- Якщо готово кілька випадків, вибір буде псевдовипадковим (без упорядкування).
- Якщо жоден кейс не готовий:
selectблокується, доки один не стане готовим. - Якщо є
default: він запускається негайно, коли жодна справа не готова (виберіть неблокуючий).
Шаблон тайм-ауту
select {
case res := <-fetch(url):
use(res)
case <-time.After(2 * time.Second):
log.Println("timeout: fetch troppo lento")
}time.After(d) повертає <-chan Time, яке створює значення після d.
Це класичний спосіб зв'язати очікування.
Неблокуючий вибір із замовчуванням
select {
case msg := <-ch:
process(msg)
default:
// niente da fare, prosegui
}Він «опитує» канал без блокування. Корисно в шляхах коду, які має залишатися чуйним без обов’язкового очікування.
Шаблон «Готово канал».
Поєднайте select з каналом скасування:
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) від координатора запускає case <-done в кожному
очікування goroutine: координоване завершення роботи.
Цикл із вибором
for {
select {
case v := <-input:
if v == nil {
return
}
handle(v)
case <-time.After(5 * time.Second):
keepAlive()
case <-ctx.Done():
return
}
}Типовий шаблон "циклу подій" для серверів і робочих.
Нульовий канал як "відключений" випадок
Просунутий трюк: case <-ch з ch == nil ніколи
вибрано. Це дозволяє динамічно «відключати» випадок:
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
}
}Коли in закрито, я обнуляю його: select продовжує обробляти лише
інші випадки.
Спробуйте
Реалізація select з двома випадками, які отримують з ch1 і ch2; надрукуйте, який прийшов першим.
Показати підказку
Синтаксис `select { case ...: ... }` з одним регістром на канал.
Рішення доступне після 3 спроб
Додайте випадок тайм-ауту до вибору за допомогою time.After(time.Second).
Показати підказку
`time.After(d)` – це канал, який отримує після d.
Рішення доступне після 3 спроб
Що робить вибірку неблокуючою?
select { case v := <-ch: ... ??? }Підведення підсумків
selectочікує на перший готовий випадок серед кількох операцій каналу.- Псевдовипадковий вибір серед кількох готових випадків.
default→ неблокуючий вибір (опитування).time.After(d)для тайм-аутів (зверніть увагу на витік у циклах: використовуйтеcontext). — Шаблони: виконаний канал, цикл подій, нульовий канал для вимкнення випадку.- Порожній
select{}= вічний блок: майже завжди помилка.