我正在尝试编写一种用于对列表进行排序的算法,并且我对Google Maps api使用网络调用(API请求)以获取有关列表中两点之间距离的信息。
我正在使用while循环,并遍历列表,直到列表的大小为0。
在每次迭代中,我都会进行一次网络调用,并在响应后从列表中删除某些内容。
我尝试将信号灯与以下代码一起使用,但无法正常工作。
let semaphore = DispatchSemaphore(value: 1)
let dispatchQueue = DispatchQueue(label: "taskQueue")
dispatchQueue.async {
while unvistedPoints.count > 0{
print("The size of the list is ", unvisited.count)
self.findNextVistablePoint(visited: visitedPoints, unvisted: unvistedPoints, completion: { (pointToVisit) in
let indexofPointToVisit = unvistedPoints.firstIndex(where: {$0 === pointToVisit})
unvistedPoints.remove(at: indexofPointToVisit!)
visitedPoints.append(pointToVisit)
semaphore.signal()
})
semaphore.wait()
}
打印语句应打印6,5,4,3,2,1。
答案 0 :(得分:0)
等待应该在网络请求之前进行。另外,实际上没有理由在这里使用dispatchQueue异步,因为您的循环工作量非常小(网络请求已经异步),并且我看不到在这里使用while循环和仅使用a即可使数组发生变异的值。代替循环。这是一个示例:
import PlaygroundSupport
import UIKit
struct StarwarsCharacter: Codable {
let name: String
}
enum APIResult<T> {
case failure(Error), success(T)
}
func getCharactersSerially(completion: @escaping (APIResult<StarwarsCharacter>) -> ()) {
var characters: [StarwarsCharacter] = []
let semaphore = DispatchSemaphore(value: 1)
let urls = (1...9).map {"https://swapi.co/api/people/\($0)"}.compactMap(URL.init(string:))
urls.forEach { url in
semaphore.wait()
print("starting request for \(url) at \(Date())")
URLSession.shared.dataTask(with: url) { data, response, error in
print("completed request for \(url) at \(Date())")
defer {
semaphore.signal()
}
guard error == nil,
let data = data,
let character = try? JSONDecoder().decode(StarwarsCharacter.self, from: data) else {
completion(.failure(error ?? NSError()))
return
}
completion(.success(character))
}.resume()
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
getCharactersSerially() { result in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let character):
print(character.name)
}
}
答案 1 :(得分:0)
下面是一些简化的操场代码,演示了如何使用信号量来确保您的请求按顺序执行:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class SomeAsyncClass {
var unvistedPoints = [ 6,5,4,3,2,1 ]
let dispatchQueue = DispatchQueue(label: "taskQueue") // serial queue
let semaphore = DispatchSemaphore(value: 1)
public func doAsyncStuff() {
for point in self.unvistedPoints {
print("Queuing point \(point)")
dispatchQueue.async {
// block before sending the network request
self.semaphore.wait()
self.makeFakeNetworkRequest(point, completion: {
// request complete
print("Completed \(point)")
self.semaphore.signal()
})
}
}
}
func makeFakeNetworkRequest(_ point:Int, completion:()->()) {
let interval = TimeInterval(exactly: (arc4random() % 3) + 1)!
print("Point \(point): Sleeping for: \(interval)")
Thread.sleep(forTimeInterval: interval)
print("Point \(point): Awoken after: \(interval)")
completion()
}
}
var c = SomeAsyncClass()
c.doAsyncStuff()
以下是输出:
Queuing point 6
Queuing point 5
Queuing point 4
Point 6: Sleeping for: 3.0
Queuing point 3
Queuing point 2
Queuing point 1
Point 6: Awoken after: 3.0
Completed 6
Point 5: Sleeping for: 3.0
Point 5: Awoken after: 3.0
Completed 5
Point 4: Sleeping for: 3.0
Point 4: Awoken after: 3.0
Completed 4
Point 3: Sleeping for: 3.0
Point 3: Awoken after: 3.0
Completed 3
Point 2: Sleeping for: 3.0
Point 2: Awoken after: 3.0
Completed 2
Point 1: Sleeping for: 3.0
Point 1: Awoken after: 3.0
Completed 1
话虽如此,这并不是最好的方法。最好使用为此目的而设计的iOS构造,即OperationQueue,它具有精细的并发控件(maxConcurrentOperationCount
),并且可以用作URLSession(delegateQueue
)的基础。我建议您使用适合您需要的结构。