Как в Go контролировать количество горутинов?
Классическая идея (ещё из C) - делать это с помощью каналов, вот как это должно быть устроено:
вы заводите канал, который будет играть роль семафора (мьютекса со счетчиком), пишете туда столько байт, сколько хотите максимум запускать сопрограмм, перед запуском каждой из них вычитываете один байт из канала, при завершении пишете его туда же. Таким нехитрым образом вы получаете ограничение сверху на число одновременно запущенных сопрограмм.
Вот пример кода:
Из вот этой книжки, рекомендую прочитать, кстати:
Как и в других многопоточных языках, уже существует достаточное колличество готовых реализаций Worker Pool (Goroutine Pool)
Вот списки пакетов на github реализующих этот функционал: ссылка и ссылка