我有一组异步执行的请求。但是,每个下一个请求只应在前一个请求完成时启动(由于数据依赖性)。
由于所有请求都应该以正确的顺序完成,DispatchGroup()
似乎没用。
我目前正在实施DispatchSemaphore()
,但我觉得这不是最佳解决方案,因为我想确保所有请求都在后台执行。
let semaphore = DispatchSemaphore(value: requests.count)
for request in requests {
apiManager().performAsyncRequest(request, failure: { error in
print(error); semaphore.signal()
}) { print(“request finished successful”)
// Next request should be performed now
semaphore.signal()
}
}
semaphore.wait()
有更好的方法来执行此操作吗?
注意:根据我遇到的其中一个答案的实现,apiManager()
不是线程安全的(由于使用了Realm数据库)。
要明确这个问题并以线程安全的方式考虑答案,并使用performAsyncRequest
的线程安全定义:
public func performAsyncRequest(_ requestNumber: Int, success: @escaping (Int) -> Void)->Void {
DispatchQueue(label: "performRequest").async {
usleep(useconds_t(1000-requestNumber*200))
print("Request #\(requestNumber) starts")
success(requestNumber)
}
}
DispatchSemaphore
let semaphore = DispatchSemaphore(value: 1)
DispatchQueue(label: "requests").async {
for requestNumber in 0..<4 {
semaphore.wait()
performAsyncRequest(requestNumber) { requestNumber in
print("Request #\(requestNumber) finished")
semaphore.signal()
}
}
}
预期输出:
Request #0 starts
Request #0 finished
Request #1 starts
Request #1 finished
Request #2 starts
Request #2 finished
Request #3 starts
Request #3 finished
使用Operation
var operations = [Operation]()
for requestNumber in 0..<4 {
let operation = BlockOperation(block: {
performAsyncRequest(requestNumber) { requestNumber in
DispatchQueue.main.sync {
print("Request #\(requestNumber) finished")
}
}
})
if operations.count > 0 {
operation.addDependency(operations.last!)
}
operations.append(operation)
}
let operationQueue = OperationQueue.main
operationQueue.addOperations(operations, waitUntilFinished: false)
输出错误:
Request #0 starts
Request #1 starts
Request #2 starts
Request #3 starts
Request #0 finished
Request #3 finished
Request #2 finished
Request #1 finished
我的感觉是,也应该可以使用Operation
,但我不知道它是否比使用DispatchSemaphore
更好。
答案 0 :(得分:1)
您可以使用NSOperationQueue
并将每个请求添加为操作
let firstOperation: NSOperation
let secondOperation: NSOperation
secondOperation.addDependency(firstOperation)
let operationQueue = NSOperationQueue.mainQueue()
operationQueue.addOperations([firstOperation, secondOperation], waitUntilFinished: false)
答案 1 :(得分:1)
您使用DispatchSemaphore处于正确的轨道上,以确保在前一个调用完成之前未启动异步调用。我只是确保管理asyncronous API调用的代码在后台运行:
let backgroundQueue = DispatchQueue(label: "requests")
let semaphore = DispatchSemaphore(value: 1)
backgroundQueue.async {
var requestNumber = 1
for request in requests {
semaphore.wait()
let currentRequestNumber = requestNumber
print("Request launched #\(requestNumber)")
apiManager().performAsyncRequest(request,
failure: {
error in
print("Request error #\(currentRequestNumber)")
semaphore.signal()
}) {
print("Request result #\(currentRequestNumber)")
semaphore.signal()
}
requestNumber = requestNumber + 1
}
}
代码将继续执行,而for循环在后台循环中运行,并在等待前一个请求完成后启动每个请求。
或者如果apiManager()不是线程安全的:
let semaphore = DispatchSemaphore(value: 1)
var requestNumber = 1
for request in requests {
semaphore.wait()
let currentRequestNumber = requestNumber
print("Request launched #\(requestNumber)")
apiManager().performAsyncRequest(request,
failure: {
error in
print("Request error #\(currentRequestNumber)")
semaphore.signal()
}) {
print("Request result #\(currentRequestNumber)")
semaphore.signal()
}
requestNumber = requestNumber + 1
}
这有一个限制,即for循环将一直执行,直到最后一个请求开始执行。但是如果你调用的代码不是线程安全的,那就没办法了。