Нужен пул воркеров, который умеет останавливаться вежливо, но не бесконечно
долго. Stop перестаёт брать новые задачи, ждёт уже начатые не дольше
timeout и возвращает ID тех, кто за это время не уложился.
Тип Job объявлен в support.go:
type Job struct {
ID int
Run func()
}
Вам нужно реализовать Pool:
// NewPool создаёт пул из workers воркеров, читающих задачи из tasks.
func NewPool(workers int, tasks <-chan Job) *Pool
// Start запускает воркеров.
func (p *Pool) Start()
// Stop прекращает приём задач и ждёт выполняющиеся не дольше timeout.
// Возвращает ID задач, которые не успели завершиться.
func (p *Pool) Stop(timeout time.Duration) []int
Требования:
- Воркеры берут задачи из
tasks и вызывают Run.
- После вызова
Stop новые задачи не начинают выполняться. Те, что уже
выполняются, получают время timeout.
- Если все выполняющиеся задачи закончились раньше
timeout, Stop возвращается
сразу, не дожидаясь конца таймаута.
- Если к концу
timeout часть задач ещё работает, Stop всё равно возвращается и
отдаёт их ID. Порядок значения не имеет.
- Остановить
Run принудительно нельзя. Опоздавшие задачи просто продолжают
работать в фоне, а пул о них больше не ждёт.
- Код безопасен для запуска под
go test -race.
Пример. Пул из двух воркеров, задача 1 работает 10 мс, задача 2 одну секунду:
tasks := make(chan Job)
p := NewPool(2, tasks)
p.Start()
tasks <- Job{ID: 1, Run: func() { time.Sleep(10 * time.Millisecond) }}
tasks <- Job{ID: 2, Run: func() { time.Sleep(time.Second) }}
time.Sleep(50 * time.Millisecond) // задача 1 уже закончилась, задача 2 в разгаре
ids := p.Stop(100 * time.Millisecond)
fmt.Println(ids) // [2]
Stop вернётся примерно через 100 мс, а не через секунду.
ID у одновременно выполняющихся задач считайте уникальными.
Граница между «принята» и «не начата» проходит по чтению из tasks. Задача, которую воркер уже вычитал из канала, считается начатой: Stop дожидается её или называет в ответе, молча выбросить её нельзя. Задачи, оставшиеся в буфере tasks, Stop не вычитывает и не запускает. Освободившийся воркер после Stop новую задачу не берёт, даже если она уже лежит в канале.
Тесты проверяют: быстрый возврат Stop, когда все задачи закончились раньше timeout (список пуст), возврат за timeout при долгой задаче и её ID в ответе, и то, что после Stop не стартует ни буферизованная, ни присланная позже задача.
Что проверяют на собеседовании: умеете ли вы ограничить ожидание остановки,
знаете ли, что функцию снаружи убить нельзя, и можете ли честно отследить, кто
ещё «в полёте».