Какой тип канала лучше использовать для асинхронного слияния в Go: буферизированный или небуферизированный?
Что проверяют
Понимаете ли вы, что буфер влияет на пропускную способность и связность горутин, но не на корректность. Следом обычно просят написать слияние (fan-in) нескольких каналов и объяснить, кто и когда закрывает выходной канал.
Как отвечать
Короткий ответ: по умолчанию небуферизированный, буфер добавляется под конкретную причину.
Как устроено слияние: на каждый входной канал своя горутина читает for v := range in и пишет в общий out, sync.WaitGroup считает эти горутины, а отдельная горутина делает wg.Wait() и затем close(out). Закрывает канал только она, иначе будет panic: close of closed channel или send on closed channel.
Чем отличаются каналы:
- небуферизированный: отправка завершается, когда получатель принял значение. Это точка синхронизации, но не гарантия, что значение уже обработано;
- буферизированный: отправка блокируется только при полном буфере, чтение только при пустом. Сглаживает всплески, когда производители и потребитель работают рывками.
Когда буфер действительно нужен:
- известное число результатов: N горутин вернут по одному значению, а потребитель может уйти раньше. Буфер размера N не даст отправителям зависнуть навсегда;
- короткие пики нагрузки: буфер развязывает производителей и потребителя. Размер подбирают замером, а не по принципу «побольше».
Если потребитель в среднем медленнее производителей, буфер любого размера заполнится, и дальше всё работает как без него. Для отмены добавьте ctx и отправку через select с веткой <-ctx.Done(), иначе при уходе потребителя горутины слияния утекут.
Где спотыкаются
Говорят, что при переполнении буфера данные теряются. Не теряются: отправитель блокируется. Потеря бывает, только если вы сами написали select с default и выбрасываете значение.
Лечат дедлок увеличением буфера. Ошибка в логике никуда не делась, она просто проявится на большем объёме данных.
Ожидают, что слияние сохранит порядок. Значения из разных входов приходят в произвольном порядке, и даже вывод fmt.Println из разных горутин может перемешаться.