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

时间:2016-10-07 09:22:17

标签: ios swift concurrency nsoperation nsoperationqueue

执行相互依赖的操作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

2 个答案:

答案 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