如何确保OperationQueue中的操作一个接一个完成 [英] How to assure that operations in an OperationQueue are finished one after another

查看:208
本文介绍了如何确保OperationQueue中的操作一个接一个完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当执行彼此依赖的操作时, OperationQueue 可用于确保以正确的顺序执行它们。

When performing operations that are dependent on each other OperationQueue could be used to assure they are executed in the correct order. However, would it also be possible to assure that operations are finished one after another?

让我们假设一个方法是异步执行的,需要一些时间才能完成:

Let's assume a method that is performed asynchronous and takes some time to finish:

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



<并创建依赖关系如下:

The operations and dependencies are created as follows:

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

This is clearly not correct. It seems that OperationQueue only assures that the operations are started in the right order (instead of finishing one after another). Although this could be performed using DispatchSemaphore, I was wondering whether it would also be possible with OperationQueue.

推荐答案

操作依赖关系正在完成,而不是启动操作,因此系统的行为正如文档中所述。问题是 DispatchQueue(标签:operations)。async - 您的 performOperation 方法退出异步分派 print ...; usleep ...;成功... ,进入为每个 performOperation 调用创建的新的分派队列。打印/睡眠/成功回调的序列然后在由Grand Central Dispatch管理的工作线程池的不同线程上执行。

Operation dependencies are on finishing, not starting operations, so the system is behaving just as documented there. The issue is the DispatchQueue(label: "operations").async – your performOperation method exits right after you inside it asynchronously dispatch the sequence of print …; usleep …; success …, into a new dispatch queue created for every performOperation call. That sequence of print / sleep / success callback then gets executed on different threads of the worker thread pool that's managed by Grand Central Dispatch.

我认为这里可能会混淆的是认为 DispatchQueue(标签:operations)给你相同的串行调度队列实例 - 这不是这样,你实际上在每个调用时间创建一个新的串行队列。

I think what you may be confused here is thinking that stating DispatchQueue(label: "operations") repeatedly would get you the same serial dispatch queue instance – this is not the case, you in fact create a new serial queue every call time.

除此之外,在 performOperation 中创建或分派到串行分派队列,因为 BlockOperation 已经实现,同时在支持OperationQueue的GCD调度队列上(并发性也可以限制< a>)。我在你的情况下会做的是构造一个新的OperationQueue与 OperationQueue()(而不是使用 OperationQueue.main which

As an aside, there's also no reason to create or dispatch to a serial dispatch queue inside your performOperation, as BlockOperation is already implemented such that the block gets executed concurrently on a GCD dispatch queue backing the OperationQueue (the concurrency is also possible to limit). What I would do in your case is construct a new OperationQueue with OperationQueue() (instead of using OperationQueue.main which dispatches work on the main queue), then asynchronously dispatch your success callbacks onto the main queue.

这个稍微修改的例子表明,操作执行确实是依赖关系(在主队列上调度工作),然后异步地将你的成功回调分派到主队列。我没有实现上述OperationQueue相关的建议,它可以说是在你提出的问题的点):

This slightly modified example shows you that the operation execution is indeed following the dependencies (I did not implement the above OperationQueue related suggestion it's arguably beside the point of the question you raised):

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

这篇关于如何确保OperationQueue中的操作一个接一个完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆