Есть код, который последовательно проверяет пачку записей. validate ходит во
внешнюю зависимость, поэтому цикл работает медленно. Его нужно переписать так,
чтобы проверки шли параллельно, но одновременно выполнялось не больше K
вызовов: больше зависимость не выдержит.
func main() {
// сделать цикл асинхронным с ограничением числа одновременных вызовов
for _, v := range fetchBatch() {
if !validate(v) {
fmt.Printf("rejected: %s\n", v)
}
}
}
func fetchBatch() []string {
return []string{"rec-1", "rec-2" /* ... */, "rec-1000"}
}
func validate(key string) bool {
return true
}
Чтобы код можно было проверить, цикл вынесен в функцию. Доработай её:
// ValidateAll проверяет каждый ключ функцией validate и возвращает отклонённые
// (validate == false) в исходном порядке. Одновременно — не больше k вызовов validate.
func ValidateAll(keys []string, k int, validate func(key string) bool) []string
Требования
validate вызывается для каждого ключа ровно один раз.
- Одновременно выполняется не больше
k вызовов validate (k < 1 считается
как 1). При этом решение действительно параллельное: если ключей хватает,
работают все k.
- Результат — отклонённые ключи в порядке входа, а не в порядке завершения
проверок. Если отклонённых нет, возвращается пустой срез (или
nil).
- Функция возвращается, когда все проверки завершены. После неё горутин не
остаётся.
- Код проходит
go test -race.
Пример
keys := []string{"rec-1", "rec-2", "rec-3", "rec-4"}
bad := ValidateAll(keys, 2, func(k string) bool {
time.Sleep(100 * time.Millisecond)
return k != "rec-2" && k != "rec-4"
})
// bad == [rec-2 rec-4], примерно через 200 мс вместо 400
Устно: события идут потоком из одной системы в другую, а вторая держит не
больше 8 одновременных соединений. Как ограничить параллелизм средствами языка?
Чем семафор на буферизованном канале отличается от пула из 8 воркеров?