Семафоры
Это похоже на хороший вариант использования семафоров.
Кредиты: https://unsplash.com/photos/5F04PN6oWeM
Позвольте мне показать вам, как это сделать с Playground, чтобы вы могли запустить это решение локально и затем импортировать его в свой проект.
Playground
Прежде всего, создайте новая пустая страница Playground и эти 3 строки, чтобы импортировать необходимые библиотеки и включить параллелизм.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
Как только вы решите перенести это решение в свой проект, вам понадобится только строка import Foundation
.
Contants
Теперь давайте определим следующие 3 константы.
Параллельные вызовы Это количество одновременных вызовов, которые вы хотите выполнить .
let concurrentCalls = 8
Семафор Этот семафор позволяет выполнять не более 8 потоков. Когда 9-й поток запрашивает доступ, он переводится в режим ожидания, пока не завершится один из 8 работающих потоков.
let semaphore = DispatchSemaphore(value: concurrentCalls)
Фоновая очередь Мы будем использовать эту очередь для асинхронной отправки всех вызовов.
let backgroundQueue = DispatchQueue(label: "Background queue")
fetchData (завершение:
Эта функция является эмуляцией вашего удаленного вызова API. Она просто ждет 3 секунды и затем вызывает завершение, передавая строку "?", которая представляет результат.
func fetchData(completion: @escaping (String) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
completion("?")
}
}
fetchAll (завершение:)
И теперь мы находимся в центре решения.
func fetchAll(completion: @escaping ([String]) -> Void) {
// 1
let storageQueue = DispatchQueue(label: "Serial queue")
var results = [String]()
let totalNumberOrCalls = 20
for i in 0..<totalNumberOrCalls {
backgroundQueue.async {
semaphore.wait()
fetchData { result in
storageQueue.async {
results.append(result)
if i == totalNumberOrCalls - 1 {
completion(results)
}
}
// 2
semaphore.signal()
}
}
}
}
Как это работает?
У нас есть Семафор со значением, установленным в 8.
Каждый раз, когда мы хотим выполнить сетевой вызов, мы спрашиваем семафор, можем ли мы начать вызывать
// 1
semaphore.wait()
Если семафор содержит значение больше 0 , затем разрешает наш удаленный вызов и уменьшает его значение.
В противном случае, если семафор содержит 0 , тогда сетевой вызов не выполнен , вместо этого он включен ждать , пока не закончится один из предыдущих вызовов.
Как только сетевые вызовы заканчиваются, мы вызываем
// 2
semaphore.signal
Таким образом, значение семафора увеличивается на 1 и разрешает выполнение другого ожидающего вызова.
Test
Теперь мы вызываем call
fetchAll { results in
print(results)
}
Не будет более 8 одновременных вызовов fetchData
и как только все вызовы будут завершены, будет напечатан результат
["?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]
Заключение
Надеюсь, это поможет, если вы хотите получить более подробную информацию, посмотрите этот ответ, где я говорю семафоры { ссылка }