如何保证OperationQueue中的操作一个接一个完成
How to assure that operations in an OperationQueue are finished one after another
执行相互依赖的操作时 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
.
操作依赖于完成操作,而不是开始操作,因此系统的行为与此处记录的一样。问题是 DispatchQueue(label: "operations").async
– 你的 performOperation
方法在你进入它之后立即退出 异步 将 print …; usleep …; success …
的序列分派到创建的新分派队列中每 performOperation
个电话。然后,打印/睡眠/成功回调序列在由 Grand Central Dispatch 管理的工作线程池的不同线程上执行。
我想你在这里可能会感到困惑的是认为重复声明 DispatchQueue(label: "operations")
会得到相同的串行调度队列实例——事实并非如此,你实际上每次调用都会创建一个新的串行队列.
顺便说一句,也没有理由在 performOperation
中创建或分派到串行分派队列,因为 BlockOperation
已经实现,因此块在 GCD 分派上同时执行队列支持 OperationQueue (the concurrency is also possible to limit)。在你的情况下我会做的是用 OperationQueue()
构造一个新的 OperationQueue (而不是使用 OperationQueue.main
在主队列上分派工作),然后将你的成功回调异步分派到主队列。
这个稍微修改过的示例向您展示了操作执行确实遵循依赖关系(我没有实施上述与 OperationQueue 相关的建议,它可以说与您提出的问题无关):
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
看这个例子优雅地理解BlockOperationExample
执行相互依赖的操作时 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
.
操作依赖于完成操作,而不是开始操作,因此系统的行为与此处记录的一样。问题是 DispatchQueue(label: "operations").async
– 你的 performOperation
方法在你进入它之后立即退出 异步 将 print …; usleep …; success …
的序列分派到创建的新分派队列中每 performOperation
个电话。然后,打印/睡眠/成功回调序列在由 Grand Central Dispatch 管理的工作线程池的不同线程上执行。
我想你在这里可能会感到困惑的是认为重复声明 DispatchQueue(label: "operations")
会得到相同的串行调度队列实例——事实并非如此,你实际上每次调用都会创建一个新的串行队列.
顺便说一句,也没有理由在 performOperation
中创建或分派到串行分派队列,因为 BlockOperation
已经实现,因此块在 GCD 分派上同时执行队列支持 OperationQueue (the concurrency is also possible to limit)。在你的情况下我会做的是用 OperationQueue()
构造一个新的 OperationQueue (而不是使用 OperationQueue.main
在主队列上分派工作),然后将你的成功回调异步分派到主队列。
这个稍微修改过的示例向您展示了操作执行确实遵循依赖关系(我没有实施上述与 OperationQueue 相关的建议,它可以说与您提出的问题无关):
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
看这个例子优雅地理解BlockOperationExample