Уроки модуля (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 в каждом
ожидающая горутина: скоординированное выключение.
Цикл с выбором
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 продолжает обрабатывать только
другие случаи.
Попробуйте это
Реализуйте выбор с двумя случаями, которые получают данные из 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{}= вечный блок: почти всегда ошибка.