> Как программа узнает об окончании списка URL и передает сигнал завершения (Go)

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

Компании: Ozon

Стек: Go

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

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

В Go программа узнаёт об окончании списка URL через сигнал завершения от источника данных - например, закрытие канала, возврат io.EOF из reader, или явный context.Cancel. Передача сигнала завершения обычно реализуется через close(ch) для каналов, defer cancel() для context, или return из функции-генератора. Это позволяет потребителю (воркеру) корректно завершить цикл обработки, не блокируясь навсегда.

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

В Go существует несколько стандартных паттернов для сигнализации об окончании последовательности данных:

  1. Каналы: producer закрывает канал через close(ch). Consumer в цикле for v := range ch автоматически завершится после закрытия. Это самый распространённый способ для потоковой передачи URL.

  2. Reader/Iterator: если URL читаются из файла, сети или БД, используется io.Reader - при достижении конца возвращается io.EOF. В цикле проверяется err == io.EOF для выхода.

  3. Context: для долгих операций с возможностью отмены используется context.Context. ctx.Done() возвращает закрытый канал при отмене или таймауте.

  4. Callback/функция-генератор: функция возвращает (url string, ok bool) - при ok == false цикл завершается.

Передача сигнала завершения должна быть однонаправленной - producer не ждёт подтверждения от consumer, а просто уведомляет. В Go это гарантируется тем, что close на канале - единственная операция, которую можно безопасно вызвать из одного горутины, и все читатели получат нулевое значение.

На практике

Для списка URL чаще всего используют комбинацию канала и sync.WaitGroup:

  • Producer (например, парсер файла или генератор) отправляет URL в канал.
  • После отправки всех URL producer вызывает close(ch).
  • Несколько воркеров читают из канала в цикле for url := range ch.
  • После завершения цикла каждый воркер вызывает wg.Done().
  • Основная горутина ждёт wg.Wait().

Если список URL приходит из внешнего источника с неизвестным концом (например, бесконечный стриминг), сигнал завершения может быть внешним - через context или отдельный канал stop.

Пример кода

GO
package main
import (
"fmt"
"sync"
)
func produce(urls []string) <-chan string {
ch := make(chan string)
go func() {
defer close(ch) // сигнал завершения
for _, u := range urls {
ch <- u
}
}()
return ch
}
func worker(id int, ch <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
for url := range ch { // завершится после close(ch)
fmt.Printf("worker %d: %s\n", id, url)
}
}
func main() {
urls := []string{"https://a.com", "https://b.com", "https://c.com"}
ch := produce(urls)
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, ch, &wg)
}
wg.Wait()
}

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

Начни с самого простого паттерна - закрытие канала и range. Затем упомяни альтернативы: io.EOF, context. Покажи понимание, почему close - правильный способ: он безопасен, идиоматичен и не требует дополнительной синхронизации. Если спросят про продюсера-потребителя - обязательно упомяни sync.WaitGroup для ожидания завершения всех воркеров. Для senior-уровня добавь рассуждение о том, как обрабатывать ошибки в середине потока: например, использовать отдельный канал ошибок или errgroup.

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

  • Понимание идиоматичного Go: close(ch) + range - стандартный паттерн.
  • Умение проектировать конкурентные pipeline без гонок.
  • Знание альтернатив: io.EOF, context, errgroup.
  • Понимание, что close - это broadcast-сигнал для всех читателей, а не только для одного.
  • Способность объяснить, почему нельзя закрывать канал из consumer (panic).

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

  • Попытка закрыть канал из consumer - вызывает panic.
  • Использование sync.WaitGroup без defer wg.Done() - deadlock.
  • Проверка if v, ok := <-ch; ok вместо for range - избыточно и менее читаемо.
  • Забывание close(ch) при ошибке в producer - воркеры заблокируются навсегда.
  • Использование time.Sleep для ожидания завершения вместо wg.Wait() - ненадёжно и недетерминировано.

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

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