Как обрабатывать данные из канала в основной горутине: использовать for loop или select в Go
Что проверяют
Понимаете ли вы семантику каналов: что делает закрытие, как ведёт себя чтение из закрытого и из nil-канала, когда select нужен, а когда только усложняет код. Следом обычно просят добавить к простому циклу отмену через context или таймаут.
Как отвечать
for v := range ch: один источник, работа до закрытия канала. Цикл завершится сам, когда отправитель вызоветclose(ch)и буфер опустеет. Закрывает канал отправитель, а не получатель;forсselect: нужен, когда источников несколько или кроме данных надо реагировать на отмену и таймер:case v, ok := <-ch:,case <-ctx.Done(): return ctx.Err(),case <-ticker.C:. Если готовы несколько веток,selectберёт случайную, приоритета нет;- закрытый канал в
select: чтение срабатывает мгновенно с нулевым значением иok == false, и без обработки цикл крутится вхолостую. Приём: присвоить переменнойnil. Чтение изnil-канала блокируется навсегда, и ветка выпадает из выбора. Когда все источники сталиnil, выходите из цикла; default: делаетselectнеблокирующим. В цикле без паузы это активное ожидание, которое съедает ядро. Уместен для «попробовать отправить, иначе сбросить».
Короткое правило: один канал без отмены даёт for range, всё остальное select. В сервисах почти всегда есть ctx, поэтому чистый range живёт в основном в воркерах пула, где сигналом остановки служит само закрытие канала.
Для слияния нескольких каналов упомяните fan-in: по горутине на источник, общий выходной канал закрывается после wg.Wait() на sync.WaitGroup.
Где спотыкаются
Проверяют конец данных через len(ch) == 0. У небуферизованного канала длина всегда ноль, а у буферизованного значение устаревает сразу после чтения.
Пишут break внутри select, рассчитывая выйти из for. Он выходит только из select, нужен return или метка и break loop.
Ставят default в цикл и получают загрузку ядра на 100% без полезной работы, либо не обрабатывают ok == false и получают бесконечный поток нулевых значений.