Реализуйте пул воркеров, который сам подстраивает число рабочих горутин под
нагрузку. Когда задач накапливается много, пул запускает дополнительных
воркеров (но не более maxWorkers); когда нагрузка спадает, лишние
простаивающие воркеры завершаются (но всегда остаётся не менее minWorkers).
type ScalingPool struct {
// ...
}
// NewScalingPool создаёт пул, читающий задачи из канала tasks. Число
// воркеров динамически меняется в диапазоне [minWorkers, maxWorkers]
// в зависимости от давления очереди.
func NewScalingPool(minWorkers, maxWorkers int, tasks <-chan func()) *ScalingPool
// Start запускает пул (минимальное число воркеров + менеджер масштабирования).
func (p *ScalingPool) Start()
// Stop корректно останавливает все воркеры и ожидает их завершения.
func (p *ScalingPool) Stop()
// Peak возвращает максимальное число одновременно работавших воркеров.
func (p *ScalingPool) Peak() int
Требования:
- После запуска и до остановки активно не более
maxWorkers и не менее
minWorkers воркеров.
- При росте бэклога задач пул масштабируется вверх; простаивающие лишние
воркеры завершаются сами.
- До остановки каждая полученная задача из
tasks выполняется ровно один раз.
Stop() завершает все горутины без утечек.
Peak() отражает пиковое число одновременных воркеров (через atomic).
На что смотрит интервьюер:
- Корректное управление жизненным циклом горутин: запуск/останов воркеров,
отсутствие утечек после
Stop() (проверка через runtime.NumGoroutine).
- Безопасное масштабирование: лишние воркеры завершаются, но граница
minWorkers соблюдается, верхняя граница maxWorkers не превышается.
- Отсутствие гонок под
-race; каждая задача выполнена ровно один раз
(атомарный счётчик).
- Использование
sync.WaitGroup, atomic-счётчиков и каналов/контекста для
координации без блокировок и дедлоков.
Границы min/max действуют после Start и до завершения работы входа или
начала Stop. Start вызывается один раз. Stop прекращает приём задач
и ждёт уже выполняющиеся функции; необработанный остаток внешней очереди
не дренируется. Чтобы выполнить весь вход, сначала дождитесь завершения
задач, затем вызывайте Stop. Каждая функция задачи должна в итоге вернуться.
Сначала попробуй сам — подсказки не дают готовый ответ.