Перейти до основного вмісту
Модуль 7 · Урок 3 із 533/50 у курсі~12 min
Модульні уроки (3/5)

`select`: мультиплексування каналів

select очікує на кілька канальних операцій одночасно. Це switch паралелізму Go: він дозволяє реагувати на перший готовий канал серед багатьох.

Синтаксис

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: він запускається негайно, коли жодна справа не готова (виберіть неблокуючий).

Шаблон тайм-ауту

Go
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. Це класичний спосіб зв'язати очікування.

Неблокуючий вибір із замовчуванням

Go
select {
case msg := <-ch:
    process(msg)
default:
    // niente da fare, prosegui
}

Він «опитує» канал без блокування. Корисно в шляхах коду, які має залишатися чуйним без обов’язкового очікування.

Шаблон «Готово канал».

Поєднайте select з каналом скасування:

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) від координатора запускає case <-done в кожному очікування goroutine: координоване завершення роботи.

Цикл із вибором

Go
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 ніколи вибрано. Це дозволяє динамічно «відключати» випадок:

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

Коли in закрито, я обнуляю його: select продовжує обробляти лише інші випадки.

Спробуйте

вправи#go.m7.l3.e1
Спроби: 0Завантаження…

Реалізація select з двома випадками, які отримують з ch1 і ch2; надрукуйте, який прийшов першим.

Завантаження редактора…
Показати підказку

Синтаксис `select { case ...: ... }` з одним регістром на канал.

Рішення доступне після 3 спроб

вправи#go.m7.l3.e2
Спроби: 0Завантаження…

Додайте випадок тайм-ауту до вибору за допомогою time.After(time.Second).

Завантаження редактора…
Показати підказку

`time.After(d)` – це канал, який отримує після d.

Рішення доступне після 3 спроб

Quiz#go.m7.l3.e3
Готовий

Що робить вибірку неблокуючою?

Go
select { case v := <-ch: ... ??? }
Варіанти відповідей

Підведення підсумків

  • select очікує на перший готовий випадок серед кількох операцій каналу.
  • Псевдовипадковий вибір серед кількох готових випадків.
  • default → неблокуючий вибір (опитування).
  • time.After(d) для тайм-аутів (зверніть увагу на витік у циклах: використовуйте context). — Шаблони: виконаний канал, цикл подій, нульовий канал для вимкнення випадку.
  • Порожній select{} = вічний блок: майже завжди помилка.