Как работает горутина для агрегирования ответов в Go
Что проверяют
Умеете ли вы запустить N параллельных запросов, собрать ответы и не оставить висящих горутин. Следом спрашивают, что будет при ошибке в одной из них и как ограничить время и число одновременных запросов.
Как отвечать
Каркас fan-in из трёх частей:
- запуск: на каждую задачу
go func() {...}(), перед запускомwg.Add(1), внутриdefer wg.Done(). В Go 1.25 то же самое делаетwg.Go(f); - сбор: горутины пишут в канал, отдельная горутина делает
wg.Wait()иclose(results), а основной код читаетfor r := range results. Закрывает канал тот, кто знает, что все писатели закончили; - порядок: если нужен порядок исходного списка, проще писать в заранее выделенный срез
out[i]. Горутины пишут в разные индексы, гонки нет, канал не нужен.
Дальше то, что отличает продовый код от учебного:
- ошибки и отмена:
errgroup.WithContext(ctx)изgolang.org/x/sync/errgroupобъединяет ожидание, первую ошибку и отмену: как только один запрос вернул ошибку, контекст остальных отменяется; - таймаут:
context.WithTimeoutи передачаctxв каждый запрос черезhttp.NewRequestWithContext; - лимит параллелизма:
g.SetLimit(10)или семафор на буферизованном канале, чтобы не открыть тысячу соединений к одному сервису; - частичный результат: если можно отдать то, что успело прийти, читайте через
selectс веткойctx.Done().
С Go 1.22 переменная цикла создаётся заново на каждой итерации, поэтому старая ловушка с захватом i в замыкании ушла, но помнить о ней на собеседовании стоит.
Где спотыкаются
Делают небуферизованный канал и перестают читать раньше времени, например на первой ошибке. Оставшиеся горутины навсегда блокируются на записи, это утечка.
Вызывают wg.Add(1) внутри самой горутины. Wait может вернуться раньше, чем счётчик увеличится.
Закрывают канал в каждой горутине-писателе и получают панику send on closed channel.