Куда помещать горутину для чтения из канала в Go
Что проверяют
Понимаете ли вы, кто владеет каналом и горутиной: кто её запускает, кто закрывает канал и как горутина завершится. Настоящий вопрос за формулировкой: где здесь дедлок или утечка. Следом часто просят найти ошибку в коротком примере.
Как отвечать
Сформулируйте правила владения:
- закрывает отправитель: канал закрывает тот, кто в него пишет, когда данных больше не будет. Читатель канал не закрывает, иначе отправитель упадёт с паникой
send on closed channel; - читатель завершается сам:
for v := range chвыходит из цикла после закрытия канала. Вselectнужна формаv, ok := <-chс выходом при!ok, а рядомcase <-ctx.Done(); - горутину запускает тот, кто её ждёт: если функция стартует горутину, она же обеспечивает её остановку через
context.Contextи ожидание черезsync.WaitGroupилиerrgroup.Group. В Go 1.25 появилсяwg.Go(func() {...}), который сам делаетAddиDone; - небуферизованный канал: отправка блокируется, пока нет читателя. Если в
mainсначала отправить, а потом запустить читающую горутину, рантайм остановит программу с ошибкой дедлока. Читателя запускают до первой отправки или отправляют из отдельной горутины.
Типичная идиома генератора: функция создаёт канал, запускает внутри горутину-писателя с defer close(out) и возвращает <-chan T. Вызывающий код читает, а жизнью писателя управляет контекст. Для пула обработчиков запускают N читающих горутин на один канал и ждут их через WaitGroup.
Ожидание через time.Sleep, как в учебных примерах, в рабочем коде не годится: это гонка, а не синхронизация.
Где спотыкаются
Читают в select через msg := <-ch без проверки ok. Закрытый канал мгновенно отдаёт нулевые значения, и горутина крутится в холостом цикле, съедая ядро.
Запускают горутину-читателя и забывают про её завершение. Канал никто не закрывает, горутина висит вечно, и с каждым запросом таких становится больше.
Закрывают канал со стороны читателя или из нескольких мест. Повторное закрытие и отправка в закрытый канал дают панику.