Как ограничить максимальное количество одновременно работающих горутин?
Что проверяют
Тот же вопрос про ограничение параллелизма, но здесь стоит показать выбор между приёмами и понимание, что именно ограничивается.
Как отвечать
Сначала уточните, что ограничивать: число одновременно работающих горутин, число одновременных обращений к внешнему ресурсу или общее число созданных горутин. Это разные вещи, и решения разные.
Семафор ограничивает одновременность, но горутины всё равно создаются на каждую задачу. Подходит, когда задач тысячи, а не миллионы:
sem := make(chan struct{}, limit)
sem <- struct{}{} // занять
defer func() { <-sem }() // освободитьПул воркеров ограничивает и одновременность, и число горутин: запускается фиксированное количество, задачи приходят по каналу.
tasks := make(chan Task)
for i := 0; i < limit; i++ {
go func() { for t := range tasks { process(t) } }()
}Он экономнее по памяти и планированию, поэтому его берут при очень больших объёмах, а также когда воркеру нужно дорогое собственное состояние, например соединение.
errgroup.SetLimit это то же ограничение, но с обработкой ошибок и отменой, и в новом коде обычно берут его.
Что добавить: число подбирается по узкому месту (пул соединений базы, лимит внешнего API, число ядер для вычислений), а не по числу задач; при остановке нужно корректно завершать воркеров закрытием канала задач и ждать их через группу ожидания; контекст даёт отмену всей пачки.
Где спотыкаются
Ставят предел «на глаз» и упираются не туда: горутин мало, а соединений к базе всё равно больше, чем в пуле.
Закрывают канал задач раньше, чем воркеры дочитали, и теряют часть работы.
Создают миллион горутин с семафором и получают всплеск памяти на самих горутинах.