Kir*_* S. 0 concurrency swift operationqueue
我确信我的逻辑有问题,只是无法弄清楚它是什么。
有一个“Service”类,它有一个操作队列:
class Service {
let queue: OperationQueue = {
var queue = OperationQueue()
queue.name = "my.operationQueue"
queue.maxConcurrentOperationCount = 1
return queue
}()
func add(operation: Operation) {
queue.addOperation(operation)
}
}
Run Code Online (Sandbox Code Playgroud)
该操作是异步的,因此它会覆盖状态和函数start
:
class MyOp: Operation {
private var state: State = .ready
private var id: Int
init(id: Int) {
self.id = id
}
override var isAsynchronous: Bool {
return true
}
override var isReady: Bool {
return state == .ready
}
override var isExecuting: Bool {
return state == .started
}
/// See: `Operation`
override var isFinished: Bool {
return state == .finished || state == .cancelled
}
/// See: `Operation`
override var isCancelled: Bool {
return state == .cancelled
}
override func start() {
guard state == .ready else {
return
}
state = .started
print("\(Date()) started \(id)")
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
self.state = .finished
print("\(Date()) finished \(self.id)")
}
}
}
private extension MyOp {
enum State {
case ready
case started
case cancelled
case finished
}
}
Run Code Online (Sandbox Code Playgroud)
我正在向队列添加多个操作(用于concurrentPerform
测试目的,实际上,它是不同的):
let iterations = 20
let service = Service()
DispatchQueue.concurrentPerform(iterations: iterations) { iteration in
let operation = MyOp(id: iteration)
service.add(operation: operation)
}
DispatchQueue.global().asyncAfter(deadline: .now() + 40) {
print("\(Date()) after run \(String(describing: service.queue.operations))")
}
Run Code Online (Sandbox Code Playgroud)
我期待什么
let iterations = 20
)queue.maxConcurrentOperationCount = 1
)实际发生了什么
操作似乎按预期添加到队列中。
我看到只有 1 个操作开始并完成,其余操作从未开始。最后一个块在添加所有操作后 40 秒打印队列内容(大约足够完成所有或几乎所有操作的时间),显示剩余操作仍在队列中,未运行。这是一个例子:
<NSOperationQueue: 0x7fd477f09460>{name = 'my.operationQueue'}
2022-03-23 21:05:51 +0000 started 11
2022-03-23 21:05:53 +0000 finished 11
2022-03-23 21:06:31 +0000 after run [
<__lldb_expr_25.MyOp 0x7fd479406660 isFinished=YES isReady=NO isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd477f04080 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd479206a70 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd460904190 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd479004080 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd479406550 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd460804080 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd470904480 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd460904080 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd460804190 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd460a04080 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd4793068c0 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd460b04080 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd477f0a160 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd460a04190 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd479406770 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd4608042a0 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd4792092f0 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd47910a360 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>,
<__lldb_expr_25.MyOp 0x7fd4609042a0 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>
]
Run Code Online (Sandbox Code Playgroud)
那么我做错了什么?
笔记:
print
,因为在实际代码中我没有使用它DispatchQueue.global().asyncAfter(deadline: .now() + 2)
- 这只是为了模拟正在运行的异步操作。更新:我将问题提炼为maxConcurrentOperationCount
:如果我删除该行queue.maxConcurrentOperationCount = 1
,队列将按预期工作。将其设置为任何其他值都会产生类似的问题。
还是不明白为什么会出错。
问题是这些方法不符合 KVC/KVO 标准。正如Operation
文档所说:
\n\n该类
\nNSOperation
的多个属性符合键值编码 (KVC) 和键值观察 (KVO) 标准。\xe2\x80\xa6
\n如果您为上述任何属性提供自定义实现,您的实现必须保持 KVC 和 KVO 合规性。
\n
对并发程度的约束(例如,maxConcurrentOperationCount
和addDependency(_:)
)依赖于 KVO 来知道先前的操作何时完成。如果未能执行所需的 KVO 通知,队列将不知道后续操作何时可以继续。
有关示例实现,请参阅尝试理解异步操作子类的后半部分。
\nFWIW,这是一个异步操作实现:
\npublic class AsynchronousOperation: Operation {\n\n @Atomic @objc private dynamic var state: OperationState = .ready\n\n // MARK: - Various `Operation` properties\n\n open override var isReady: Bool { state == .ready && super.isReady }\n public final override var isExecuting: Bool { state == .executing }\n public final override var isFinished: Bool { state == .finished }\n public final override var isAsynchronous: Bool { true }\n\n // KVO for dependent properties\n\n open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {\n if [#keyPath(isReady), #keyPath(isFinished), #keyPath(isExecuting)].contains(key) {\n return [#keyPath(state)]\n }\n\n return super.keyPathsForValuesAffectingValue(forKey: key)\n }\n\n // Start\n\n public final override func start() {\n if isCancelled {\n state = .finished\n return\n }\n\n state = .executing\n\n main()\n }\n\n /// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception.\n\n open override func main() {\n fatalError("Subclasses must implement `main`.")\n }\n\n /// Call this function to finish an operation that is currently executing\n\n public final func finish() {\n if !isFinished { state = .finished }\n }\n}\n\nprivate extension AsynchronousOperation {\n /// State for this operation.\n\n @objc enum OperationState: Int {\n case ready\n case executing\n case finished\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n具有以下内容:
\n@propertyWrapper\npublic class Atomic<T> {\n private var _wrappedValue: T\n private let lock = NSLock()\n\n public var wrappedValue: T {\n get { lock.withLock { _wrappedValue } }\n set { lock.withLock { _wrappedValue = newValue } }\n }\n\n public init(wrappedValue: T) {\n _wrappedValue = wrappedValue\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n通过上述内容,我将异步Operation
代码抽象为可以子类化并继承异步行为的代码。例如,这是一个与您的示例执行相同操作的操作asyncAfter
(但有一些额外的OSLog
路标,以便我可以直观地看到仪器中的操作):
import os.log\n\nprivate let log = OSSignposter(subsystem: "Op", category: .pointsOfInterest)\n\nclass MyOperation: AsynchronousOperation {\n var value: Int\n\n init(value: Int) {\n self.value = value\n super.init()\n }\n\n override func main() {\n let id = log.makeSignpostID()\n let name: StaticString = "Operation"\n let state = log.beginInterval(name, id: id, "\\(value)")\n \n DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [self] in\n finish()\n log.endInterval(name, state, "\\(value)")\n }\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n然后 ...
\nlet queue = OperationQueue()\nqueue.maxConcurrentOperationCount = 1\n\nfor i in 0..<5 {\n queue.addOperation(MyOperation(value: i))\n}\n
Run Code Online (Sandbox Code Playgroud)\n...产生如下操作的时间表:
\n\n 归档时间: |
|
查看次数: |
834 次 |
最近记录: |