Нужен пул воркеров, в котором важные задачи обгоняют менее важные. Свободный воркер всегда берёт из очереди задачу с наибольшим приоритетом. Вызывающий код при этом не ждёт: Submit кладёт задачу в очередь и сразу возвращается.
Тип Task уже объявлен в support.go, вам нужно реализовать PriorityPool:
type Task struct {
Priority int // 1..3, где 3 - наивысший
Run func()
}
// NewPriorityPool создаёт пул из workers воркеров.
func NewPriorityPool(workers int) *PriorityPool
// Submit добавляет задачу в очередь и не блокирует вызывающего.
func (p *PriorityPool) Submit(t Task)
// Start запускает воркеры.
func (p *PriorityPool) Start()
// Stop выполняет все уже принятые задачи и останавливает воркеры.
func (p *PriorityPool) Stop()
Единственный воркер занят долгой задачей. Пока он занят, в очередь приходят задачи с приоритетами 1, 3, 1, 3, 2. Когда воркер освободится, он выполнит их в порядке 3, 3, 2, 1, 1:
pool := NewPriorityPool(1)
pool.Start()
pool.Submit(Task{Priority: 2, Run: func() { time.Sleep(100 * time.Millisecond) }}) // занимает воркера
for _, p := range []int{1, 3, 1, 3, 2} {
p := p
pool.Submit(Task{Priority: p, Run: func() { fmt.Println("приоритет", p) }})
}
pool.Stop()
// приоритет 3
// приоритет 3
// приоритет 2
// приоритет 1
// приоритет 1
- Воркер выбирает из очереди задачу с наибольшим
Priority. Среди задач с одинаковым приоритетом разумно сохранить порядок поступления (тесты его не проверяют, но интервьюер спросит).
Submit не ждёт ни свободного воркера, ни места в очереди: очередь растёт сколько нужно. Тест отправляет 1000 задач подряд.
Stop дожидается всех задач, принятых до его вызова, и завершает воркеры. После Stop в пуле не остаётся живых горутин.
- Очередь строится на
container/heap либо на корзинах по приоритетам, а воркеры на пустой очереди спят на sync.Cond, а не крутят цикл с Sleep.
- Код проходит
go test -race.
Тесты проверяют порядок 3, 3, 2, 1, 1 при занятом воркере, что Submit не ждёт (5000 задач при заблокированном единственном воркере), что Stop выполняет все принятые задачи при отправке из восьми горутин, что после Stop нет лишних горутин и что три взаимно ждущие задачи завершаются только на трёх параллельных воркерах. Порядок внутри одного приоритета и то, что воркеры спят на sync.Cond, тесты не проверяют.
Порядок вызовов фиксирован: сначала Start, затем Submit. Все вызовы Submit завершаются до Stop, после Stop новые задачи не отправляют. Функции Run рано или поздно возвращаются.