Есть заранее известный набор задач и параметр workers. Нужно, чтобы задачи
разбирали ровно workers воркеров: каждый берёт следующую свободную задачу,
выполняет её, берёт следующую. На собесе это обычно звучит так: «10 задач,
каждая спит секунду и печатает номер воркера, который её выполнил; число
воркеров — параметр запуска». Вместо печати вернём номера воркеров.
// Run выполняет все tasks силами workers воркеров и возвращает,
// какой воркер выполнил какую задачу: who[i] — номер воркера (0..workers-1)
// для tasks[i]. workers < 1 считается как 1.
func Run(workers int, tasks []func()) (who []int)
Требования
- Каждая задача выполняется ровно один раз;
Run возвращается, когда
выполнены все. len(who) == len(tasks).
- Воркеров ровно
workers — это долгоживущие горутины, а не горутина на
каждую задачу. Одновременно выполняется не больше workers задач, и
если задач хватает, работают все воркеры.
- Воркер выполняет свои задачи последовательно: две задачи с одинаковым
who[i] никогда не идут одновременно.
- После возврата
Run горутин не остаётся. Код проходит go test -race.
Пример
tasks := make([]func(), 10)
for i := range tasks {
tasks[i] = func() { time.Sleep(time.Second) }
}
who := Run(3, tasks) // ≈ 4 с: ⌈10/3⌉ «волны» по секунде
fmt.Println(who) // например [0 2 1 0 1 2 2 0 1 0]
Устно: сколько займёт пример при 1, 3, 10 и 100 воркерах и почему?
Чем этот пул отличается от варианта «горутина на задачу + семафор»? Как
передать номер воркера в саму задачу?
Разбор и эталонное решение — после своей попытки: на собеседовании подсказок не будет.
Сначала попробуй сам — подсказки не дают готовый ответ.