Когда завершится цикл for при чтении из канала в Go
Что проверяют
Понимаете ли вы, что for v := range ch завершается только закрытием канала, и кто этот канал должен закрывать. Следом обычно просят показать выход по отмене через context и спрашивают, что будет, если канал никто не закроет.
Как отвечать
Цикл for range по каналу заканчивается, когда канал закрыт и все значения из буфера прочитаны. Закрытие данные не теряет: буферизованный канал сначала отдаст всё, что в нём лежит. По сути это ручная проверка v, ok := <-ch с выходом при ok == false. Остальные способы выйти явные: break, return, goto или паника.
Если канал не закрыт и отправителей больше нет, цикл блокируется навсегда. Когда уснули все горутины программы, рантайм падает с фатальной ошибкой о дедлоке (all goroutines are asleep). В сервисе, где живы другие горутины, падения не будет: получится тихая утечка горутины. Цикл по nil-каналу блокируется сразу и навсегда.
Правила закрытия стоит назвать сразу:
- закрывает отправитель: получатель не знает, будут ли ещё данные;
- несколько отправителей: каждый вызывает
wg.Done(), а отдельная горутина ждётwg.Wait()и делаетclose(ch); - паники: повторный
closeи отправка в закрытый канал паникуют, а чтение из закрытого канала нет, оно сразу возвращает нулевое значение.
Для отмены используют select в бесконечном for: одна ветка читает v, ok := <-ch, другая ждёт <-ctx.Done() и возвращает ctx.Err(). Если читаете из нескольких каналов, закрытый канал можно присвоить nil, и select перестанет его выбирать.
Где спотыкаются
Пишут break внутри select и ждут выхода из for. break выходит только из select, для цикла нужна метка или return.
Закрывают канал на стороне получателя, чтобы «остановить» отправителя. Его следующая отправка паникует.
Забывают, что закрытый канал в select всегда готов к чтению. Без проверки ok цикл крутится на нулевых значениях и съедает процессор.