OperationQueue с пользовательским `maxConcurrentOperationCount` не берет/выполняет все операции в очереди после завершения первой операции
Я уверен, что с моей логикой что-то не так, просто не могу понять, что это такое.
Есть класс "Сервис", который имеет очередь операций:
class Service {
let queue: OperationQueue = {
var queue = OperationQueue()
queue.name = "my.operationQueue"
queue.maxConcurrentOperationCount = 1
return queue
}()
func add(operation: Operation) {
queue.addOperation(operation)
}
}
Операция является асинхронной, поэтому она переопределяет состояния и
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
}
}
Я добавляю несколько операций в очередь (используя
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))")
}
Что я ожидаю
- 20 операций добавляются в очередь (т.к.
let iterations = 20
) - 1 операция начинает выполняться сразу, остальные ждут в очереди (потому что )
- после завершения первой операции начинается вторая и так далее.
- последний блок, который печатает содержимое очереди, не должен содержать никаких элементов или максимум 1-2 оставшихся элемента.
Что на самом деле происходит
Операции добавляются в очередь, как и ожидалось.
Я вижу, что только 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>
]
Так что я делаю неправильно?
Примечание:
- Это не проблема с
print
неправильно, так как в реальном коде я его не использую - Также в реальном коде нет
DispatchQueue.global().asyncAfter(deadline: .now() + 2)
- это просто для имитации запущенной асинхронной операции.
Обновление : я решил проблему
maxConcurrentOperationCount
: если я удалю строку
queue.maxConcurrentOperationCount = 1
, очередь работает как положено. Установка любого другого значения создает аналогичную проблему.
До сих пор не понимаю, почему это неправильно.