Таймаут и отмена
Клиент пожаловался, что загрузка стоит. Разбор показал — запрос к S3 завис, и весь конвейер встал следом. Миша поставил таймаут на запрос, и конвейер впервые заработал в проде.
два самых частых применения select — таймаут через time.After и отмена через done-канал, закрытие которого будит всех ожидающих.
Таймаут запроса к S3
select {
case resp := <-s3Ch:
fmt.Println("S3 ответил:", resp)
case <-time.After(3 * time.Second):
fmt.Println("таймаут")
}
time.After возвращает канал, который получит значение через заданное время. Select ждёт либо данных от S3, либо таймера — что раньше. Читать из канала с ограничением времени — базовый приём для внешних вызовов, иначе загрузка может висеть вечно.
Отмена через done-канал
done := make(chan struct{})
go func() {
select {
case <-done:
return
case f := <-queue:
process(f)
}
}()
close(done) // отмена загрузки: все, кто ждёт done, просыпаются
Закрытый канал — готов всегда, поэтому <-done немедленно срабатывает у всех ожидающих. Закрытие — это широковещательный сигнал: одна операция будит тысячу горутин. В уроке этот паттерн вырастет в context.
Без приоритета
Ещё деталь: когда done закрыт и одновременно пришла задача, готовы оба case, и select выбирает случайно. Отмена не имеет приоритета — после close(done) воркер может успеть обработать ещё пару задач. Если нужна жёсткая гарантия «после close — ничего», отменяющая сторона ждёт завершения воркеров (sync.WaitGroup, следующий урок). Промежуточный вариант — проверять done отдельным if перед select.
Вы узнаете:
- как
time.Afterв цикле вёл себя до и после Go 1.23 - почему закрытый канал в select срабатывает мгновенно
Таймаут — time.After в select; отмена — закрытие done-канала, будящее всех; при равенстве готовности выбор случаен.
10 000 ₽ · 60 минут · онлайн