Ограничьте число одновременных загрузок до N через task group
Загрузите множество URL, но держите не более maxConcurrent запросов в любой момент, переиспользуя существующую func fetch(_ url: URL) async throws -> Data.
Требования:
- Не более
maxConcurrentдочерних задач одновременно. - Обработайте весь список; верните результаты (порядок входа не обязателен).
func download(_ urls: [URL], maxConcurrent: Int) async throws -> [Data] {
// ваш код здесь
}
Допишите реализацию.
Заполните группу первыми N задачами; когда результат приходит, добавляйте следующий элемент. Так в работе всегда N — не более N дочерних задач — что ограничивает память и число соединений. Добавите все задачи сразу — запустятся все разом.
- ✗Считают, что task group сам ограничивается числом ядер
- ✗Блокируют дочерние задачи семафором вместо дросселирования добавлений
- ✗Думают, что нельзя управлять числом одновременных задач группы
- →Что происходит с остатком очереди, если одна дочерняя задача бросает ошибку?
- →Как сохранить порядок входа, ограничивая параллелизм числом N?
Группа держит не более window дочерних задач: каждый group.next() освобождает слот, и вы добавляете следующий URL, поэтому параллелизм остаётся равным N:
func download(_ urls: [URL], maxConcurrent: Int) async throws -> [Data] {
try await withThrowingTaskGroup(of: Data.self) { group in
var results: [Data] = []
var next = 0
let window = min(maxConcurrent, urls.count)
for _ in 0..<window { // засеять N
let url = urls[next]; next += 1
group.addTask { try await fetch(url) }
}
while let data = try await group.next() { // один вышел — один вошёл
results.append(data)
if next < urls.count {
let url = urls[next]; next += 1
group.addTask { try await fetch(url) }
}
}
return results
}
}
Поскольку это структурированная группа, брошенная ошибка автоматически отменяет остальные дочерние задачи. Добавьте все задачи сразу — и каждая загрузка стартует одновременно; именно это данный приём и предотвращает.