如何限制for循环中的网络调用数量?

问题描述 投票:1回答:1

我在服务器上为每个用户提供了一些对象,我必须全部获取它们。首先,我从服务器获取对象计数,以计算我必须进行30次批处理的网络调用数。假设有2000个对象,则我必须进行的调用数为2000/30 = 67。我为第一个调用设置了offset = 0和limit = 29,然后将offset和limit都增加了30,并进行了第二个调用,依此类推。问题是,如果我在一个for循环中同时发送8个以上的调用,则这些调用会开始失败,即使我重试它也会继续失败。我正在使用Alamofire将发布请求发送到服务器。我注意到,如果我并行发送8个请求,一切似乎都很好。如何确保并行进行8个呼叫,并且在完成全部8个呼叫后,再将8个发送给服务器。

这是一段代码:

 let count = Double(totalTransactionsCount)
  //The maximum amount of transactions to fetch in one call.
  //let maxLimit = 30
  let calls = Int(ceil(count/Double(maxLimit)))

 for call in 1...calls {
        print("Calling call \(call) with offset:\(offset) to limit:\(limit)")
        callFetchTransactionWith(offset, limit)

        offset += maxLimit
        limit += maxLimit
    }


 fileprivate func callFetchTransactionWith(_ offset: Int, _ limit: Int, _ callCountPercentage: Double, _ calls: Int) {
    TransactionsModel.reportTransactions(offset: offset, limit: limit ,success: { (transactions) in
        ACTIVITYCALL += 1
        self.currentSyncingProgress = self.currentSyncingProgress + CGFloat(callCountPercentage)
        Utility.logger(log: "\(self.currentSyncingProgress)", message: "Activity Increment Progress")
        if ACTIVITYCALL == calls {
            TransactionsModel.assignSequenceNumbers()
            self.didCompleteProgressAndSync(duration: 2.0)
            return
        } else {
            self.updateProgressBar(value: self.currentSyncingProgress)
        }
    }, failure: { (response, statusCode) in
        print(response,statusCode)
        self.callFetchTransactionWith(offset, limit, callCountPercentage, calls)
    })
}


static func reportTransactions(offset:Int,limit:Int,
    success:@escaping ( _ model: [TransactionsModel] ) -> Void,
    failure:@escaping APIClient.FailureHandler) {

    let params:Parameters = [
        "offset":offset,
        "limit":limit
    ]

    let headers:[String:String] = [
        "X-Auth-Token":Singleton.shared.token
    ]

    if !Connectivity.isConnectedToInternet {
        Constants.UIWindow?.showErrorHud(text: AppString.internetUnreachable)
        return
    }

    APIClient.shared().requestPost(endpoint: Route.reportTransactions, params: params, headers: headers, success: { (response) in
        guard
            let data = response as? [String:Any],
            let transactions = data["transactions"] as? [[String : Any]]
            else {return}

        let transactionModels = Mapper<TransactionsModel>().mapArray(JSONArray: transactions)
        TransactionsModel.save(transactions: transactionModels)

        success(transactionModels)
    }) { (response, status) in
        print(response,status)
        failure(response,status)
    }
}
ios swift xcode alamofire
1个回答
0
投票

信号量

这看起来是信号量的好用例。

enter image description here信用:https://unsplash.com/photos/5F04PN6oWeM

让我向您展示如何使用Playground进行操作,以便您可以在本地运行此解决方案,然后将其导入到您的项目中。

游乐场

首先创建一个新的空白Playground页面和这3行,以导入所需的库并启用并发。

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

一旦决定将此解决方案移至您的项目,则只需要import Foundation行。

内容

现在让我们定义以下三个常量。

并发通话这是您要执行的并发调用数。

let concurrentCalls = 8

信号量此信号量将允许执行不超过8个线程。当第9个线程请求访问时,它将进入等待状态,直到8个线程之一运行完毕。

let semaphore = DispatchSemaphore(value: concurrentCalls)

后台队列我们将使用此队列异步调度所有调用。

let backgroundQueue = DispatchQueue(label: "Background queue")

fetchData(completion:

此函数是您的远程API调用的仿真。它只等待3秒钟,然后通过表示结果的“🎁”字符串调用完成。

func fetchData(completion: @escaping (String) -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
        completion("🎁")
    }
}

fetchAll(completion:)

现在我们是解决方案的核心。

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)
                    }
                }
                semaphore.signal()
            }
        }
    }
}

如何运作?

我们有一个设置为8的信号灯。

每次我们都想进行网络通话时,我们都会询问信号量是否可以开始通话

// 1
semaphore.wait()

如果信号量的值大于0,则它允许我们的远程调用并减小其值。否则,如果信号量保持为0,则网络调用为未执行,而是将其置于等待状态,直到先前的调用之一结束。网络通话结束后,我们就会致电

// 2
semaphore.signal

此等待信号量值增加1,并且确实允许执行另一个等待中的调用。

测试

现在我们打电话给电话

fetchAll { results in
    print(results)
}

fetchData的并发调用将不超过8个,并且所有调用完成后,将立即打印结果

["🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁", "🎁"]

结论

希望这会有所帮助,如果您想了解更多详细信息,请在我谈论信号量https://stackoverflow.com/a/51816661/1761687的地方查看此答案>

© www.soinside.com 2019 - 2024. All rights reserved.