如何确保OperationQueue中的操作一个接一个地完成

执行相互依赖的OperationQueue ,可以使用OperationQueue来确保以正确的顺序执行它们。 但是,也可以保证操作一个接一个地完成?

让我们假设一个执行asynchronous的方法,需要一些时间来完成:

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

操作和依赖关系创build如下:

 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

操作依赖关系正在完成,而不是开始操作,所以系统的行为就像那里logging的那样。 问题是DispatchQueue(label: "operations").async – 你的performOperation方法在你进入后立即退出, asynchronous调度print …; usleep …; success …的顺序print …; usleep …; success … print …; usleep …; success … print …; usleep …; success … ,为每个执行操作调用创build一个新的调度队列。 然后,在Grand Central Dispatchpipe理的工作线程池的不同线程上执行print / sleep / successcallback序列。

我想这里你可能会感到困惑的是,认为反复声明DispatchQueue(label: "operations")会得到相同的串行调度队列实例 – 事实并非如此,实际上你在每次调用时创build一个新的串行队列。

BlockOperation说一下,在执行操作中没有任何理由创build或分配到一个串行调度队列,因为已经实现了BlockOperation ,使得该块在支持OperationQueue的GCD调度队列上同时执行( 并发也可以限制 ) 。 我会做你的情况是构造一个新的OperationQueue与OperationQueue() (而不是使用OperationQueue.main派遣工作在主队列),然后asynchronous调度您的成功callback到主队列。

这个稍微修改过的例子显示了操作的执行确实遵循了依赖关系(我没有实现上面的OperationQueue相关的build议,它可以说是在你提出的问题的旁边):

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

这将输出…

 Operation #1 should finish after Operation #0 Operation #2 should finish after Operation #1 Operation #3 should finish after Operation #2 Operation #4 should finish after Operation #3 Operation #5 should finish after Operation #4 Operation #6 should finish after Operation #5 Operation #7 should finish after Operation #6 Operation #0 starts Operation #0 finished Operation #1 starts Operation #1 finished Operation #2 starts Operation #2 finished Operation #3 starts Operation #3 finished Operation #4 starts Operation #4 finished Operation #5 starts Operation #5 finished Operation #6 starts Operation #6 finished Operation #7 starts Operation #7 finished