执行相互依赖的操作OperationQueue
时,可以确保它们以正确的顺序执行。但是,是否也可以确保操作一个接一个地完成?
让我们假设一个异步执行的方法,需要一些时间才能完成:
public func performOperation(_ number: Int, success: @escaping (Int) -> Void)->Void {
DispatchQueue(label: "operations").async {
print("Operation #\(number) starts")
usleep(useconds_t(1000-number*200)) // Block thread for some time
success(number)
}
}
操作和依赖关系创建如下:
let operationQueue = OperationQueue.main
for operationNumber in 0..<4 { // Create operations as an example
let operation = BlockOperation(block: {
performOperation(operationNumber) { number in
DispatchQueue.main.sync {
print("Operation #\(number) finished")
}
}
})
operation.name = "Operation #\(operationNumber)"
if operationNumber > 0 {
operation.addDependency(operationQueue.operations.last!)
// Print dependencies
print("\(operation.name!) should finish after \(operation.dependencies.first!.name!)")
}
operationQueue.addOperation(operation)
}
使用以下输出:
Operation #1 should finish after Operation #0
Operation #2 should finish after Operation #1
Operation #3 should finish after Operation #2
Operation #0 starts
Operation #1 starts
Operation #2 starts
Operation #3 starts
Operation #0 finished
Operation #3 finished
Operation #2 finished
Operation #1 finished
这显然不正确。似乎OperationQueue
仅确保操作以正确的顺序启动(而不是一个接一个地完成)。
虽然可以使用DispatchSemaphore
执行此操作,但我想知道是否也可以使用OperationQueue
。
答案 0 :(得分:7)
操作依赖关系正在完成,而不是启动操作,因此系统的行为就像在那里记录的那样。问题是DispatchQueue(label: "operations").async
- 您的performOperation
方法在您内部退出异步将print …; usleep …; success …
的序列调度到为每个{创建的新调度队列中“后立即退出{1}}致电。然后,在Grand Central Dispatch管理的工作线程池的不同线程上执行该序列的打印/睡眠/成功回调。
我认为你在这里可能会感到困惑的是认为反复声明performOperation
会使你获得相同的串行调度队列实例 - 事实并非如此,你实际上每次调用时都会创建一个新的串行队列。 / p>
顺便说一句,也没有理由创建或调度到DispatchQueue(label: "operations")
内的串行调度队列,因为performOperation
已经实现,以便在GCD调度队列支持上同时执行该块OperationQueue(the concurrency is also possible to limit)。在我的例子中,我要做的是使用BlockOperation
构造一个新的OperationQueue(而不是使用OperationQueue()
调度主队列上的工作),然后异步将成功回调调度到主队列。
这个稍微修改过的示例向您展示了操作执行确实遵循了依赖关系(我没有实现上面与OperationQueue相关的建议,它可能与您提出的问题无关):
OperationQueue.main
这将输出......
public func performOperation(_ number: Int, success: @escaping (Int) -> Void)->Void {
print("Operation #\(number) starts")
usleep(useconds_t(1000-(number*50))) // Block thread for some time
success(number)
}
…
let operationQueue = OperationQueue.main
for operationNumber in 0..<8 { // Create operations as an example
let operation = BlockOperation(block: {
self.performOperation(operationNumber) { number in
print("Operation #\(number) finished")
}
})
operation.name = "Operation #\(operationNumber)"
if operationNumber > 0 {
operation.addDependency(operationQueue.operations.last!)
// Print dependencies
print("\(operation.name!) should finish after \(operation.dependencies.first!.name!)")
}
operationQueue.addOperation(operation)
}
答案 1 :(得分:0)
请参阅此示例以优雅地了解BlockOperation Example