1

我正在尝试使用 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()
        
    }
4

3 回答 3

3

异步函数的问题是您永远无法知道块返回的顺序。如果您需要保留订单,请使用如下索引:

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"
}

同样在您的示例中,您dispatchGroup.leave()甚至在异步块完成之前调用。这也会产生错误的结果。

于 2020-08-20T07:40:51.537 回答
2

使用信号量消除所有并发可以解决顺序问题,但会带来很大的性能损失。Dennis 有一个正确的想法,即与其牺牲并发性,不如对结果进行排序。

话虽如此,我可能会使用字典:

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)。这是一个单独的问题。但我建议不要牺牲并发性只是为了以正确的顺序获得结果。

于 2021-06-14T17:59:49.953 回答
1

在这种情况下,使用DispatchSemaphore

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()
    }
}
于 2020-08-20T07:59:07.937 回答