Объединение семафоров для ограничения асинхронных запросов и групп рассылки для .notify с отсрочкой, и приложение зависает - PullRequest
0 голосов
/ 29 мая 2019

У меня есть бэкэнд, который может обрабатывать до 4 одновременных сетевых подключений. Чтобы компенсировать это, я создал массив с именем urlsQueue, который содержит все параметры, необходимые для каждого сетевого запроса. Приведенный ниже код дает желаемые результаты, если urlsQueue содержит 4 или менее элементов. Однако, если я введу func downloadBusiness с 5+ элементами внутри urlsQueue, приложение зависнет.

func downloadBusinesses(latitude: Double, longitude: Double){
    if urlsQueue.isEmpty {return}
    let semaphore = DispatchSemaphore(value: 4)
    let dispatchGroup = DispatchGroup()

    for (index, element) in urlsQueue.enumerated(){
        dispatchGroup.enter()
        semaphore.wait()
        _ = Client.getBusinesses(latitude: latitude, longitude: longitude, offset: element.offset ,completion: { [weak self] (yelpDataStruct, result) in
            defer {
                semaphore.signal()
                dispatchGroup.leave()
            }
            self?.handleGetNearbyBusinesses(inputData: yelpDataStruct, result: result)
        })
    }
    dispatchGroup.notify(queue: .main) {[weak self] in
        self?.runDownloadAgain()
    }
}

Если сетевой запрос завершается успешно, я удаляю соответствующее значение из urlsQueue. В случае сбоя сетевого запроса запись остается внутри urlsQueue. После перебора каждого элемента в urlsQueue я повторяю процесс снова, если цикл не пустой. Таким образом, я могу повторно выполнить любые неудачные сетевые запросы.

func runDownloadAgain(){
    let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] timer in
        self?.downloadBusinesses(latitiude: self!.latitude, longitude: self!.longitude)
    }
    timer.fire()
}

Мне удалось отладить точку останова в приложении в цикле for-enumerated выше. После 4-го раза в цикле все просто останавливается. Если я прохожу программу, я не могу двигаться вперед. Если я настраиваю жестко запрограммированное значение 4 для любого другого целого числа, проблема повторяется, и кодовый блок успешно выполняется только в том случае, если urlsQueue.count <= x, где x - это значение в <code>DispatchSemaphore(value: x)

Проблема заключается в моем неправильном смешивании dispatchGroup / semaphore / defer. Но я действительно изо всех сил пытаюсь понять, как можно успешно объединить эти элементы, чтобы избежать зависания приложения при ограничении сетевых подключений до 4.

1 Ответ

1 голос
/ 29 мая 2019

Поскольку вы всегда начинаете с нулевого индекса в очереди URL, выйдите из цикла, если индекс> 3, и отбросьте семафор

func downloadBusinesses(latitiude: Double, longitude: Double){
    if urlsQueue.isEmpty {return}
    let dispatchGroup = DispatchGroup()

    for (index, element) in urlsQueue.enumerated(){
        if index > 3 { break }
        dispatchGroup.enter()
        _ = Client.getBusinesses(latitude: latitude, longitude: longitude, offset: element.offset ,completion: { [weak self] (yelpDataStruct, result) in
            defer {
                dispatchGroup.leave()
            }
            self?.handleGetNearbyBusinesses(inputData: yelpDataStruct, result: result)
        })
    }
    dispatchGroup.notify(queue: .main) { // GCD closures don't cause retain cycles
        self.runDownloadAgain()
    }
}
...