前言
NSOperation是Foundation中用于多线程并发任务的抽象类,它可以放到NSOperationQueue任务队列中去执行。它底层还是基于GCD来实现的,相比GCD它支持取消操作和依赖管理。
Apple用swift重写了Foundation库并且开源,可以通过阅读NSOperation的swift源代码来了解其实现原理。
在分析源代码之前也请阅读一下苹果的《Concurrency Programming Guide》的Operation Queues一章,要了解如何使用Operation。
同步和异步
NSOperation是抽象类,使用它需要子类继承它,并覆写一些成员方法。
它有两种模式,一种是同步执行,一种是异步执行。
同步执行,需要覆写main方法即可。
@interface MyOperation : NSOperation
@end
@implementation MyOperation
- (void)main {
// 要实现的业务逻辑写这里即可
}
@end
当main方法返回的时候,任务也就结束了。
异步执行,需要覆写start方法,同时实现asynchronous、concurrent、executing和finished属性。
@interface MyOperation : NSOperation {
BOOL _executing;
BOOL _finished;
}
@property (nonatomic, assign, getter = isExecuting) BOOL executing;
@property (nonatomic, assign, getter = isFinished) BOOL finished;
@end
@implementation MyOperation
@synthesize executing = _executing;
@synthesize finished = _finished;
- (void)start {
// 要实现的业务逻辑写这里即可
}
- (BOOL)isAsynchronous {
return YES;
}
- (BOOL)isConcurrent {
return YES;
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
@end
当start方法返回的时候,任务可以继续执行,直到任务真正执行完毕,我们执行self.finished=YES; self.executing=YES;通过KVO的方式通知NSOperation任务已经执行完毕,可以从NSOperationQueue中移除此任务了。
OperationQueue
OperationQueue是任务的执行队列
初始化
OperationQueue有两个初始化方法:
一个是public方法给大家创建任务队列使用的
open class OperationQueue : NSObject, ProgressReporting {
// 任务队列名字
var __name: String?
// 对外的初始化方法
public override init() {
super.init()
__name = "NSOperationQueue (Unmanaged<OperationQueue>.passUnretained(self).toOpaque())"
}
}
另一个是internal方法,专门用来初始化mainQueue的
extension OperationQueue {
public static let defaultMaxConcurrentOperationCount: Int = -1
}
open class OperationQueue : NSObject, ProgressReporting {
// 是否是主线程队列
var __mainQ: Bool = false
// 并发数上限
var __maxNumOps: Int = OperationQueue.defaultMaxConcurrentOperationCount
// 实际并发数上限
var __actualMaxNumOps: Int32 = .max
// 任务队列名字
var __name: String?
// QOS,任务队列的优先级
var __propertyQoS: QualityOfService?
// asMainQueue是一个空元组,只是为了重载init方法,方便内部使用
internal init(asMainQueue: ()) {
super.init()
__mainQ = true
__maxNumOps = 1
__actualMaxNumOps = 1
__name = "NSOperationQueue Main Queue"
#if canImport(Darwin)
__propertyQoS = QualityOfService(qos_class_main())
#else
__propertyQoS = QualityOfService.userInteractive
#endif
}
// 返回主线程任务队列
open class var main: OperationQueue {
get {
struct Once {
// 使用static保证了变量只初始化一次
static let mainQ = OperationQueue(asMainQueue: ())
}
return Once.mainQ
}
}
}
添加任务
添加一个Operation到任务队列中,对外提供了两个方法,可以添加单个任务或者批量任务。
open class OperationQueue : NSObject, ProgressReporting {
// 添加单个任务
open func addOperation(_ op: Operation) {
_addOperations([op], barrier: false)
}
// 添加多个任务,同时支持设置是否同步等待
open func addOperations(_ ops: [Operation], waitUntilFinished wait: Bool) {
_addOperations(ops, barrier: false)
if wait {
for op in ops {
op.waitUntilFinished()
}
}
}
}
对内提供了一个下划线开头的批量添加任务方法,所有的添加任务,都最终会走到此方法中
open class OperationQueue : NSObject, ProgressReporting {
internal func _addOperations(_ ops: [Operation], barrier: Bool = false) {
// 判空保护
if ops.isEmpty { return }
// 失败数
var failures = 0
// 成功数
var successes = 0
// 第一个新任务
var firstNewOp: Unmanaged<Operation>?
// 上一个新任务
var lastNewOp: Unmanaged<Operation>?
// 循环传入的任务数组
for op in ops {
// 对op的状态进行CAS修改,从initialized修改成enqueuing
if op._compareAndSwapState(.initialized, .enqueuing) {
// 如果修改成功,成功数累加1
successes += 1
// 判断之前的修改是否都成功
if 0 == failures {
// 走到这里,说明所有的任务的状态都正确修改成enqueuing
// retain一下任务对象
let retained = Unmanaged.passRetained(op)
// 如果任务的没有未完成的依赖任务,则为isReady为YES,缓存到_cachedIsReady
op._cachedIsReady = op.isReady
// GCD的工作对象
let schedule: DispatchWorkItem
// 创建GCD的工作对象
if let qos = op.__propertyQoS?.qosClass {
// 任务有设置过QoS,enforceQoS表示使用传入的qos
schedule = DispatchWorkItem.init(qos: qos, flags: .enforceQoS, block: {
// 当工作对象放入DispatchQueue的时候,会开始执行此block
// self是OperationQueue对象,执行其_schedule方法,来调度任务
self._schedule(op)
})
} else {
// 任务没有设置过Qos,assignCurrentContext表示使用当前线程的Qos
schedule = DispatchWorkItem(flags: .assignCurrentContext, block: {
// 重复代码,同上
self._schedule(op)
})
}
// 调用Operation的_adopt方法,设置__queue和__schedule成员变量
op._adopt(queue: self, schedule: schedule)
// 任务队列实际上是双向链表存储,设置任务的前后链表节点
op.__previousOperation = lastNewOp
op.__nextOperation = nil
if let lastNewOperation = lastNewOp?.takeUnretainedValue() {
lastNewOperation.__nextOperation = retained
} else {
firstNewOp = retained
}
lastNewOp = retained
} else {
// 前面的任务状态有修改失败的场景,回滚当前任务的状态修改
_ = op._compareAndSwapState(.enqueuing, .initialized)
}
} else {
// 如果修改失败,失败数累加1
failures += 1
}
}
// 判断是否有任务入队失败
if 0 < failures {
// 因为有失败情况发生,要回滚前面的操作,firstNewOp就是双向链表的头节点
while let firstNewOperation = firstNewOp?.takeUnretainedValue() {
// 取出下一个节点
let nextNewOp = firstNewOperation.__nextOperation
// 对当前任务对象做重置操作
firstNewOperation._invalidateQueue()
// 清理链接节点指针
firstNewOperation.__previousOperation = nil
firstNewOperation.__nextOperation = nil
// 回滚任务的状态
_ = firstNewOperation._compareAndSwapState(.enqueuing, .initialized)
// 释放任务
firstNewOp?.release()
// 继续处理后继节点
firstNewOp = nextNewOp
}
// 回滚完,还是要报fatalError
fatalError("operations finished, executing or already in a queue cannot be enqueued")
}
// Attach any operations pending attachment to main list
// 非阻塞任务
if !barrier {
// 加锁,因为后续要修改OperationQueue的成员变量
_lock()
// OperationCount自增1,这里自增1不是很对啊
_incrementOperationCount()
}
// 前面已经处理好任务的状态,并生成了一个双向链表
// firstNewOp就是链表头,lastNewOp就是链表最后一个元素
// __firstOperation和__lastOperation是OperationQueue已经存在的双向链表
// 这里是要把此次传入的任务链表,挂到OperationQueue的双向链表上去
var pending = firstNewOp
if let pendingOperation = pending?.takeUnretainedValue() {
let old_last = __lastOperation
pendingOperation.__previousOperation = old_last
if let old = old_last?.takeUnretainedValue() {
old.__nextOperation = pending
} else {
__firstOperation = pending
}
__lastOperation = lastNewOp
}
// pending就是firstNewOp,是链表表头,这里就是循环处理任务链表
while let pendingOperation = pending?.takeUnretainedValue() {
if !barrier {
// 如果任务中有要阻塞的barrie任务,是通过把它添加到依赖任务中,来实现的
var barrierOp = _firstPriorityOperation(Operation.QueuePriority.barrier)
while let barrierOperation = barrierOp?.takeUnretainedValue() {
pendingOperation._addDependency(barrierOperation)
barrierOp = barrierOperation.__nextPriorityOperation
}
}
// 任务的状态从enqueuing改成enqueued
_ = pendingOperation._compareAndSwapState(.enqueuing, .enqueued)
// 根据__priorityValue和__propertyQoS设置一下任务的优先级
// 如果未设置过优先级,默认是Operation.QueuePriority.normal级别
var pri = pendingOperation.__priorityValue
if pri == nil {
let v = __actualMaxNumOps == 1 ? nil : pendingOperation.__propertyQoS
if let qos = v {
switch qos {
case .default: pri = Operation.QueuePriority.normal.rawValue
case .userInteractive: pri = Operation.QueuePriority.veryHigh.rawValue
case .userInitiated: pri = Operation.QueuePriority.high.rawValue
case .utility: pri = Operation.QueuePriority.low.rawValue
case .background: pri = Operation.QueuePriority.veryLow.rawValue
}
} else {
pri = Operation.QueuePriority.normal.rawValue
}
}
// 按照任务的优先级,还有一个单链表,链表节点字段是Operation的__nextPriorityOperation
// 单链表的头节点和尾节点是OperationQueue的__firstPriorityOperation和__lastPriorityOperation
// 它俩是元组对象,包含所有级别的单链表的头节点和尾节点
pendingOperation.__nextPriorityOperation = nil
if let old_last = _lastPriorityOperation(pri)?.takeUnretainedValue() {
old_last.__nextPriorityOperation = pending
} else {
_setFirstPriorityOperation(pri!, pending)
}
_setlastPriorityOperation(pri!, pending)
// 循环下一个链表
pending = pendingOperation.__nextOperation
}
// 非阻塞任务
if !barrier {
// 解锁,任务添加完毕
_unlock()
}
// 阻塞任务由调用方实现调度逻辑
if !barrier {
// 调度任务
_schedule()
}
}
}
调度任务
internal func _schedule() {
// 重试任务数组
var retestOps = [Operation]()
// 因为要对OperationQueue操作,所以要先加锁
_lock()
// 可用的槽位数
var slotsAvail = __actualMaxNumOps - __numExecOps
// 要循环一下优先级单链表上的任务
for prio in Operation.QueuePriority.priorities {
// 如果没有可用的槽位,或者当前任务队列被暂停,则跳出循环
if 0 >= slotsAvail || _suspended {
break
}
// 根据任务优先级获取任务优先级单链表的表头
var op = _firstPriorityOperation(prio)
// 前驱节点,移除当前节点的时候要使用
var prev: Unmanaged<Operation>?
while let operation = op?.takeUnretainedValue() {
// 保护判断一下
if 0 >= slotsAvail || _suspended {
break
}
// 后继节点
let next = operation.__nextPriorityOperation
// 是否要重试
var retest = false
// if the cached state is possibly not valid then the isReady value needs to be re-updated
// 如果任务状态已经是enqueued,并且没有未完成的依赖,则开始执行此任务
if Operation.__NSOperationState.enqueued == operation._state && operation._fetchCachedIsReady(&retest) {
// 从任务优先级单链表上移除此任务
if let previous = prev?.takeUnretainedValue() {
previous.__nextPriorityOperation = next
} else {
_setFirstPriorityOperation(prio, next)
}
if next == nil {
_setlastPriorityOperation(prio, prev)
}
// 已经从单链表上移除,清空后继节点
operation.__nextPriorityOperation = nil
// 状态修改成dispatching
operation._state = .dispatching
// __numExecOps自增1
_incrementExecutingOperations()
// 可用的槽位数减1
slotsAvail -= 1
// 选择GCD的Queue
let queue: DispatchQueue
if __mainQ {
// 如果是主线程,则使用主线程的GCD的Queue
queue = DispatchQueue.main
} else {
// 如果未设置__dispatch_queue,会根据QoS合成一个对应的GCD DispatchQueue
queue = __dispatch_queue ?? _synthesizeBackingQueue()
}
// 取出任务的GCD工作对象
if let schedule = operation.__schedule {
if operation is _BarrierOperation {
// 如果是Barrier阻塞任务,则调用GCD的时候也设置barrier参数
queue.async(flags: .barrier, execute: {
schedule.perform()
})
} else {
// 异步执行任务
queue.async(execute: schedule)
}
}
// 处理后续任务
op = next
} else {
// 如果任务还未准备好
if retest {
// 放到重试数组中
retestOps.append(operation)
}
// 继续循环后续任务
prev = op
op = next
}
}
}
// 解锁
_unlock()
// 尝试重新计算未准备好的任务的ready状态
for op in retestOps {
if op.isReady {
op._cachedIsReady = true
}
}
}
DispatchWorkItem的block会执行OperationQueue的_schedule(_ op: Operation)方法,就是下面这个方法,这个block是_addOperations的时候设置的
internal func _schedule(_ op: Operation) {
// 设置任务状态为starting
op._state = .starting
// set current tsd
// 这样设置以后,在当前线程中的start方法可以找到OperationQueue的引用,Operation没有对外暴露其引用
// 就是OperationQueue.current
OperationQueue._currentQueue.set(self)
// 开始执行Operation的start方法
op.start()
// 重置currentQueue
OperationQueue._currentQueue.clear()
// We've just cleared _currentQueue storage.
// NSThreadSpecific doesn't release stored value on clear.
// This means `self` will leak if we don't release manually.
Unmanaged.passUnretained(self).release()
// 如果任务的isFinished返回YES,但是状态还不是finished,触发一下KVO,结束当前任务
// 这个属于补偿逻辑,一般设置了finished = YES,按理说就应该触发其KVO
// unset current tsd
if op.isFinished && op._state.rawValue < Operation.__NSOperationState.finishing.rawValue {
Operation.observeValue(forKeyPath: _NSOperationIsFinished, ofObject: op)
}
}
执行任务
在同步模式的时候,还有一个_execute方法会执行
internal func _execute(_ op: Operation) {
// 同步模式可以反馈任务进度
var operationProgress: Progress? = nil
if !(op is _BarrierOperation) && _isReportingProgress {
let opProgress = Progress(parent: nil, userInfo: nil)
opProgress.totalUnitCount = 1
progress.addChild(opProgress, withPendingUnitCount: 1)
operationProgress = opProgress
}
operationProgress?.becomeCurrent(withPendingUnitCount: 1)
defer { operationProgress?.resignCurrent() }
// 执行任务的main方法
op.main()
}
结束任务
internal func _operationFinished(_ op: Operation, _ previousState: Operation.__NSOperationState) {
// There are only three cases where an operation might have a nil queue
// A) The operation was never added to a queue and we got here by a normal KVO change
// B) The operation was somehow already finished
// C) the operation was attempted to be added to a queue but an exception occured and was ignored...
// Option C is NOT supported!
// 判断任务是否是阻塞任务
let isBarrier = op is _BarrierOperation
// 加锁
_lock()
// 取后续任务,移除链表的时候要使用
let nextOp = op.__nextOperation
// 判断任务状态是否已结束
if Operation.__NSOperationState.finished == op._state {
// 任务已执行完,讲任务移除链表
let prevOp = op.__previousOperation
if let prev = prevOp {
prev.takeUnretainedValue().__nextOperation = nextOp
} else {
__firstOperation = nextOp
}
if let next = nextOp {
next.takeUnretainedValue().__previousOperation = prevOp
} else {
__lastOperation = prevOp
}
// only decrement execution count on operations that were executing! (the execution was initially set to __NSOperationStateDispatching so we must compare from that or later)
// else the number of executing operations might underflow
// 减少正在执行的任务数
if previousState.rawValue >= Operation.__NSOperationState.dispatching.rawValue {
_decrementExecutingOperations()
}
// 重置任务的前后链表节点
op.__previousOperation = nil
op.__nextOperation = nil
// 重置任务队列
op._invalidateQueue()
}
// 减少任务数
if !isBarrier {
_decrementOperationCount()
}
// 解锁
_unlock()
// 在调度一下其他任务
_schedule()
// 释放任务
if previousState.rawValue >= Operation.__NSOperationState.enqueuing.rawValue {
Unmanaged.passUnretained(op).release()
}
}
Operation
任务状态
Operation有一个__state成员变量,表示当前任务状态,__NSOperationState枚举有8个值,初始化的时候initialized,每次对__state状态修改,都有__atomicLoad锁来保护。
open class Operation : NSObject {
// 任务状态枚举
enum __NSOperationState : UInt8 {
case initialized = 0x00
case enqueuing = 0x48
case enqueued = 0x50
case dispatching = 0x88
case starting = 0xD8
case executing = 0xE0
case finishing = 0xF0
case finished = 0xF4
}
// 任务状态
internal var __state: __NSOperationState = .initialized
// 负责同步任务状态的锁
var __atomicLoad = NSLock()
// 任务状态的get和set方法都由__atomicLoad来同步
internal var _state: __NSOperationState {
get {
__atomicLoad.lock()
defer { __atomicLoad.unlock() }
return __state
}
set(newValue) {
__atomicLoad.lock()
defer { __atomicLoad.unlock() }
__state = newValue
}
}
// CAS方法,用来修改任务状态
internal func _compareAndSwapState(_ old: __NSOperationState, _ new: __NSOperationState) -> Bool {
__atomicLoad.lock()
defer { __atomicLoad.unlock() }
if __state != old { return false }
__state = new
return true
}
}
初始化
Operation的init方法是一个空实现,子类实现的时候可以重载init方法,带一些业务数据字段,将来可以在start方法中使用,比如,下载的链接地址NSURL对象。
open class Operation : NSObject {
public override init() { }
}
start
OperationQueue调度任务执行的时候,最终会执行任务的start方法,如果是异步模式,子类是要覆写start方法的,如果是同步模式,start方法不用动,start方法内部最终会调用main方法。
open func start() {
let state = _state
// 判断一下任务的状态,如果已经结束就直接返回
if __NSOperationState.finished == state { return }
// 判断任务是否是重复执行
if !_compareAndSwapState(__NSOperationState.initialized, __NSOperationState.starting) && !(__NSOperationState.starting == state && __queue != nil) {
switch state {
case .executing: fallthrough
case .finishing:
fatalError("(self): receiver is already executing")
default:
fatalError("(self): something is trying to start the receiver simultaneously from more than one thread")
}
}
// 任务状态是已入队,但是还没有ready,就开始执行,也要报错
if state.rawValue < __NSOperationState.enqueued.rawValue && !isReady {
_state = state
fatalError("(self): receiver is not yet ready to execute")
}
// 判断任务是否已取消
let isCanc = _isCancelled
if !isCanc {
// 任务未取消,状态改成executing开始执行
_state = .executing
Operation.observeValue(forKeyPath: _NSOperationIsExecuting, ofObject: self)
// 如果任务放到了队列中,就调用队列的_execute方法,否则直接执行main方法
_queue?._execute(self) ?? main()
}
// 结束任务
if __NSOperationState.executing == _state {
_state = .finishing
Operation.observeValue(forKeyPath: _NSOperationIsExecuting, ofObject: self)
Operation.observeValue(forKeyPath: _NSOperationIsFinished, ofObject: self)
} else {
_state = .finishing
Operation.observeValue(forKeyPath: _NSOperationIsFinished, ofObject: self)
}
}
KVO监控处理
internal static func observeValue(forKeyPath keyPath: String, ofObject op: Operation) {
enum Transition {
case toFinished
case toExecuting
case toReady
}
// 根据keyPath来设置kind
let kind: Transition?
if keyPath == _NSOperationIsFinished || keyPath == _NSOperationIsFinishedAlternate {
kind = .toFinished
} else if keyPath == _NSOperationIsExecuting || keyPath == _NSOperationIsExecutingAlternate {
kind = .toExecuting
} else if keyPath == _NSOperationIsReady || keyPath == _NSOperationIsReadyAlternate {
kind = .toReady
} else {
kind = nil
}
if let transition = kind {
switch transition {
case .toFinished: // we only care about NO -> YES
// 触发了isFinished KVO
if !op.isFinished {
// 误触发,isFinished返回NO,先返回
return
}
// 以及ready的依赖任务
var ready_deps = [Operation]()
op._lock()
let state = op._state
// 打印警告信息,任务还未开始执行,就已经设置了isFinished KVO
if op.__queue != nil && state.rawValue < __NSOperationState.starting.rawValue {
print("*** (type(of: op)) (Unmanaged.passUnretained(op).toOpaque()) went isFinished=YES without being started by the queue it is in")
}
if state.rawValue < __NSOperationState.finishing.rawValue {
op._state = .finishing
} else if state == .finished {
op._unlock()
return
}
let down_deps = op.__downDependencies
op.__downDependencies.removeAll()
if 0 < down_deps.count {
for down in down_deps {
let idown = down.contents.takeUnretainedValue()
idown._lock()
if idown._unfinishedDependencyCount == 1 {
ready_deps.append(idown)
} else if idown._unfinishedDependencyCount > 1 {
idown._decrementUnfinishedDependencyCount()
} else {
assert(idown._unfinishedDependencyCount == 0)
assert(idown._isCancelled == true)
}
idown._unlock()
}
}
// 任务结束,修改状态
op._state = .finished
// 重置任务队列引用
let oq = op.__queue
op.__queue = nil
// 是否任务锁
op._unlock()
if 0 < ready_deps.count {
for down in ready_deps {
down._lock()
if down._unfinishedDependencyCount >= 1 {
down._decrementUnfinishedDependencyCount()
}
down._unlock()
Operation.observeValue(forKeyPath: _NSOperationIsReady, ofObject: down)
}
}
// waitUntilFinished方法可以返回了
op.__waitCondition.lock()
op.__waitCondition.broadcast()
op.__waitCondition.unlock()
// 如果设置__completion回调,这异步执行一下回调
if let complete = op.__completion {
let held = Unmanaged.passRetained(op)
DispatchQueue.global(qos: .default).async {
complete()
held.release()
}
}
// 从任务队列中移除已经完成的任务
if let queue = oq {
queue.takeUnretainedValue()._operationFinished(op, state)
queue.release()
}
case .toExecuting:
let isExecuting = op.isExecuting
op._lock()
// 更新状态为正在执行
if op._state.rawValue < __NSOperationState.executing.rawValue && isExecuting {
op._state = .executing
}
op._unlock()
case .toReady:
// 任务已经ready,调度执行一下任务
let r = op.isReady
op._cachedIsReady = r
let q = op._queue
if r {
q?._schedule()
}
}
}
}
总结
以上就是Operation的核心处理流程,通读一遍源代码,对理解Operation Queue有非常大的帮助。