> Куда помещать горутину для чтения из канала в Go (Go)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: Wildberries

Стек: Go

> Пример ответа

Короткий ответ

Горутину для чтения из канала обычно размещают там, где она логически принадлежит: в функции, которая владеет каналом, или в отдельной функции-воркере, запускаемой через go. Если канал создаётся в одной функции, а читается в другой - читатель запускается в той функции, которая отвечает за жизненный цикл данных. Для паттерна worker pool чтение размещают в методе воркера, а управление запуском - в диспетчере.

Подробное объяснение

В Go нет жёсткого правила "куда класть горутину", но есть принципы, которые определяют выбор:

  • Владение каналом: тот, кто создаёт канал, обычно отвечает за его закрытие и запуск читателя. Это снижает риск утечек и дедлоков.
  • Логическая группировка: если чтение - часть обработки сообщения, горутина живёт рядом с обработчиком. Если чтение - фоновая задача, её запускают в отдельной функции.
  • Управление жизненным циклом: если нужно останавливать чтение через context или done-канал, горутина должна быть запущена в том месте, где доступен этот сигнал.
  • Масштабируемость: для N воркеров чтение размещают в функции воркера, а не в главной функции, иначе придётся дублировать код.

Типичные варианты:

  1. В той же функции, где создан канал - когда канал локальный и используется для сбора результатов.
  2. В отдельной функции-читателе - когда чтение - самостоятельная задача, например, обработка очереди задач.
  3. В методе структуры - когда воркер инкапсулирует канал и логику чтения.

На практике

Для senior-позиции важно показать, что выбор места зависит от архитектуры:

  • Если канал - часть публичного API пакета, читатель - это отдельная функция, которую вызывает пользователь.
  • Если канал внутренний, читатель - приватный метод, запускаемый в конструкторе или через Start().
  • При использовании errgroup или sync.WaitGroup горутины запускаются там, где собираются результаты, чтобы корректно дождаться завершения.

Также стоит упомянуть: горутину для чтения не стоит запускать в init() или в глобальной области - это усложняет тестирование и контроль завершения.

Пример кода

GO
// Вариант 1: чтение в той же функции, где создан канал
func process(data []int) []int {
ch := make(chan int)
go func() {
defer close(ch)
for _, v := range data {
ch <- v * 2
}
}()
var result []int
for v := range ch {
result = append(result, v)
}
return result
}
// Вариант 2: отдельный воркер с управлением через done
type Worker struct {
jobs <-chan Job
done chan struct{}
}
func (w *Worker) Run() {
go w.readLoop()
}
func (w *Worker) readLoop() {
defer close(w.done)
for job := range w.jobs {
job.Process()
}
}
// Вариант 3: чтение в отдельной функции, вызываемой пользователем
func Consume(ctx context.Context, ch <-chan Message) {
go func() {
for {
select {
case <-ctx.Done():
return
case msg, ok := <-ch:
if !ok {
return
}
handleMessage(msg)
}
}
}()
}

Как отвечать на собеседовании

Начните с принципа владения каналом, затем приведите 2-3 конкретных сценария. Обязательно упомяните управление завершением через context или done-канал. Покажите, что выбор места влияет на тестируемость и читаемость. Если спросят про паттерн worker pool - объясните, что горутина чтения живёт в воркере, а не в диспетчере. Не уходите в абстракции, приведите короткий пример из практики.

Что проверяет интервьюер

  • Понимание модели конкурентности Go и правил владения каналами.
  • Умение проектировать жизненный цикл горутин без утечек.
  • Способность выбирать между разными паттернами (inline, worker, отдельная функция).
  • Знание идиоматичных подходов: for range по каналу, select с ctx.Done().
  • Умение объяснить trade-off между простотой и контролем.

Типичные ошибки

  • Запуск горутины чтения в init() или в глобальной области - сложно тестировать.
  • Чтение из канала в главной функции без WaitGroup - программа завершится раньше, чем обработаются данные.
  • Запуск горутины в функции, которая не владеет каналом - риск закрытия канала в одном месте и чтения в другом без синхронизации.
  • Игнорирование сигнала завершения - горутина висит вечно после закрытия канала.
  • Создание горутины на каждое сообщение вместо одного цикла чтения - лишние аллокации и сложность управления.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью