问题描述
我正在尝试使用for in循环功能获取一堆数据,但是它没有以正确的顺序返回数据。看起来某些数据需要花费更长的时间来获取,因此它们被混合在一个阵列中,在该阵列中,我需要以正确的顺序存储所有数据。因此,我使用了dispatchGroup。但是,它不起作用。您能告诉我我在做什么错吗?花了10多个小时来寻找解决方案...下面是我的代码。
@IBAction func parseXMLTapped(_ sender: Any) {
let codeArray = codes[0]
for code in codeArray {
self.fetchData(code)
}
dispatchGroup.notify(queue: .main) {
print(self.dataToAddArray)
print("Complete.")
}
}
private func fetchData(_ code: String) {
dispatchGroup.enter()
print("count: \(count)")
let dataParser = DataParser()
dataParser.parseData(url: url) { (dataItems) in
self.dataItems = dataItems
print("Index #\(self.count): \(self.dataItems)")
self.dataToAddArray.append(self.dataItems)
}
self.dispatchGroup.leave()
dispatchGroup.enter()
self.count += 1
dispatchGroup.leave()
}
解决方法
异步函数的问题是,您永远无法知道块返回的顺序。 如果您需要保留订单,请使用如下索引:
sum(rate(myNiceMetric[1d])*60*60*24) by (result,component)
在您的示例中,您甚至在异步块结束之前就调用let dispatchGroup = DispatchGroup()
var dataToAddArray = [String](repeating: "",count: codeArray.count)
for (index,code) in codeArray.enumerated() {
dispatchGroup.enter()
DataParser().parseData(url: url) { dataItems in
dataToAddArray[index] = dataItems
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
print("Complete"
}
。那也会产生错误的结果。
在这种情况下,使用DispathSemaphore:
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global().async {
for code in codeArray {
self.fetchData(code)
semaphore.wait()
}
}
private func fetchData(_ code: String) {
print("count: \(count)")
let dataParser = DataParser()
dataParser.parseData(url: url) { (dataItems) in
self.dataItems = dataItems
print("Index #\(self.count): \(self.dataItems)")
self.dataToAddArray.append(self.dataItems)
semaphore.signal()
}
}
,
使用信号量消除所有并发可以解决顺序问题,但会带来很大的性能损失。 Dennis has the right idea,即不牺牲并发性,而是对结果进行排序。
话虽如此,我可能会使用字典:
let group = DispatchGroup()
var results: [String: [DataItem]] // you didn't say what `dataItems` was,so I'll assume it's an array of `DataItem` objects; but this detail isn't material to the broader question
for code in codes {
group.enter()
DataParser().parseData(url: url) { dataItems in
results[code] = dataItems // if parseData doesn't already uses the main queue for its completion handler,then dispatch these two lines to the main queue
group.leave()
}
}
group.notify(queue: .main) {
let sortedResults = codes.compactMap { results[$0] } // this very efficiently gets the results in the right order
// do something with sortedResults
}
现在,我可能会建议限制并发度(例如,也许您想将其限制为 CPU 的数量或某个合理的固定数量(例如 4 或 6)。这是一个单独的问题。但我建议不要牺牲并发只是为了以正确的顺序获得结果。