Как реализовать воркер пул в Go, чтобы запустить N горутин и дождаться завершения всех задач
Что проверяют
Владеете ли вы базовым паттерном конкурентности в Go: каналы, sync.WaitGroup и правило, кто закрывает канал. Следом почти всегда просят собрать результаты и ошибки, а затем отменить всю работу по первой ошибке или по таймауту.
Как отвечать
Опишите три части пула:
- канал задач: воркеры читают его через
for job := range jobs. Цикл завершается, когда канал закрыт и опустел, поэтому отправитель после последней задачи вызываетclose(jobs); - N воркеров:
wg.Add(1)вызывается доgo, внутри воркераdefer wg.Done(). Начиная с Go 1.25 естьwg.Go(func()), который делает то же самое сам; - канал результатов: его закрывает отдельная горутина,
go func() { wg.Wait(); close(results) }(), а основной код в это время читаетrange results.
Для отмены передайте в воркер context.Context и читайте задачи через select с веткой <-ctx.Done(). На практике чаще берут errgroup из golang.org/x/sync: errgroup.WithContext отменяет контекст при первой ошибке, g.SetLimit(n) ограничивает число одновременных горутин, а g.Wait() возвращает первую ошибку. Это тот же пул, только без ручной возни с каналами.
Размер пула выбирают от ресурса: для вычислений около runtime.GOMAXPROCS(0), для сетевых вызовов больше, но не выше лимита внешней системы, например размера пула соединений к базе. Паника в любой горутине роняет весь процесс, поэтому при недоверенных задачах воркер оборачивают в recover.
Где спотыкаются
Вызывают wg.Add(1) внутри горутины. Wait может вернуться раньше, чем горутина успеет увеличить счётчик, и программа завершится с недоделанной работой.
Закрывают канал со стороны получателя или дважды. Отправка в закрытый канал и повторный close дают панику, поэтому закрывает всегда единственный отправитель.
Ловят дедлок на результатах: основной код сначала ждёт wg.Wait(), а потом читает небуферизованный канал результатов. Воркеры висят на отправке, Wait не возвращается, и рантайм падает с сообщением «all goroutines are asleep». Тот же итог, если забыть close(jobs).