前言
前面整理了RxSwift的subscribeOn与observeOn源码。本文将重点总结RxSwift的Scheduler,以日常开发中使用频率较高的ConcurrentDispatchQueueScheduler、MainScheduler为主。前文也提到过,subscribeOn与observeOn只是基于开发者设置的scheduler在流程上进行线程调度的控制。这里可以理解为相对于subscribeOn与observeOn,scheduler只是它们的工具。(ps:本文基于RxSwift 5.1.1)
ImmediateSchedulerType&SchedulerType
ImmediateSchedulerType及SchedulerType两个协议为Scheduler类指定了规则。SchedulerType继承自ImmediateSchedulerType。在RxSwift中,各Scheduler类大部分继承自SchedulerType协议,也有只继承ImmediateSchedulerType的。
SchedulerType协议主要在扩展了两个定时触发的方法scheduleRelative、schedulePeriodic。
// ImmediateSchedulerType.swift
public protocol ImmediateSchedulerType {
func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable
}
// SchedulerType.swift
public protocol SchedulerType: ImmediateSchedulerType {
/// - returns: Current time.
var now : RxTime {
get
}
func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable
func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable
}
源码:
ConcurrentDispatchQueueScheduler
ConcurrentDispatchQueueScheduler的创建可直接传入一个qos,即我们熟悉的GCD中DispatchQoS。源码中会帮我们创建一个DispatchQueue,属性设置为并发,即ConcurrentDispatchQueueScheduler内管理着一个GCD并发队列。
当然,这里的构造方法还可以直接传入一个DispatchQueue。这意味着我们也可以传一个串行队列进去(虽然这种做法不太合理,哈哈...)。
通过构造方法,我们也能看到,DispatchQueue最后是被托管到一个DispatchQueueConfiguration中了。DispatchQueueConfiguration才是真正调用DispatchQueue进行线程调度的地方。
// ConcurrentDispatchQueueScheduler.swift
public class SerialDispatchQueueScheduler : SchedulerType
ConcurrentDispatchQueueScheduler.init(qos: .userInteractive)
@available(iOS 8, OSX 10.10, *)
public convenience init(qos: DispatchQoS, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
self.init(queue: DispatchQueue(
label: "rxswift.queue.\(qos)",
qos: qos,
attributes: [DispatchQueue.Attributes.concurrent],
target: nil),
leeway: leeway)
}
public init(queue: DispatchQueue, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
self.configuration = DispatchQueueConfiguration(queue: queue, leeway: leeway)
}
源码:
DispatchQueueConfiguration
结构体DispatchQueueConfiguration属性如下,关于DispatchQueue的调用定义在它的扩展代码中,这个我们后面会讲到。
struct DispatchQueueConfiguration {
let queue: DispatchQueue
let leeway: DispatchTimeInterval
}
这里先让我们会想一下,在讲订阅的时候,代码最终会走到SubscribeOnSink.run()方法。这里会触发scheduler的线程调度:
// SubscribeOn.swift - class SubscribeOnSink
func run() -> Disposable {
let disposeEverything = SerialDisposable()
let cancelSchedule = SingleAssignmentDisposable()
disposeEverything.disposable = cancelSchedule
let disposeSchedule = self.parent.scheduler.schedule(()) { _ -> Disposable in
let subscription = self.parent.source.subscribe(self)
disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription)
return Disposables.create()
}
cancelSchedule.setDisposable(disposeSchedule)
return disposeEverything
}
假如我们设置的scheduler为ConcurrentDispatchQueueScheduler,则它的schedule方法为如下代码。内部调用的是DispatchQueueConfiguration.schedule方法。最终会将传入的闭包抛到队列异步执行。
可以看到,ConcurrentDispatchQueueScheduler的多线程逻辑最终依赖的是GCD。
// ConcurrentDispatchQueueScheduler.swift
public final func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
return self.configuration.schedule(state, action: action)
}
// DispatchQueueConfiguration.swift
func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
let cancel = SingleAssignmentDisposable()
self.queue.async {
if cancel.isDisposed {
return
}
cancel.setDisposable(action(state))
}
return cancel
}
这里顺带讲一下scheduleRelative方法,这是一个定时触发的线程调度(还有一个schedulePeriodic方法是可以执行重复触发的逻辑,可类比该方法)。从DispatchQueueConfiguration的源码中可以看到,这里利用的是GCD的DispatchSourceTimer进行定时。
比较有意思的是,这里的DispatchSourceTimer不需要全局持有也能正常使用,作者也加了TODO注释。笔者特意去查看了最新版的RxSwift,并没有看到代码的更新。如果有知道的朋友可以下方讨论一下。
// ConcurrentDispatchQueueScheduler.swift
public final func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
return self.configuration.scheduleRelative(state, dueTime: dueTime, action: action)
}
// DispatchQueueConfiguration.swift
func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
let deadline = DispatchTime.now() + dueTime
let compositeDisposable = CompositeDisposable()
let timer = DispatchSource.makeTimerSource(queue: self.queue)
timer.schedule(deadline: deadline, leeway: self.leeway)
// TODO:
// This looks horrible, and yes, it is.
// It looks like Apple has made a conceputal change here, and I'm unsure why.
// Need more info on this.
// It looks like just setting timer to fire and not holding a reference to it
// until deadline causes timer cancellation.
var timerReference: DispatchSourceTimer? = timer
let cancelTimer = Disposables.create {
timerReference?.cancel()
timerReference = nil
}
timer.setEventHandler(handler: {
if compositeDisposable.isDisposed {
return
}
_ = compositeDisposable.insert(action(state))
cancelTimer.dispose()
})
timer.resume()
_ = compositeDisposable.insert(cancelTimer)
return compositeDisposable
}
源码:
SerialDispatchQueueScheduler
在讲MainScheduler之前,先来看看SerialDispatchQueueScheduler,因为MainScheduler继承自SerialDispatchQueueScheduler。和ConcurrentDispatchQueueScheduler逻辑类似,只不过它主要是作为串行队列的逻辑封装。
// MainScheduler.swift
public final class MainScheduler : SerialDispatchQueueScheduler
因为都是继承自SchedulerType,所以SerialDispatchQueueScheduler与ConcurrentDispatchQueueScheduler逻辑代替相同,这里就不过多赘述了。DispatchQueue队列也是托管给DispatchQueueConfiguration管理的,逻辑与上述相同。
// SerialDispatchQueueScheduler.swift
public class SerialDispatchQueueScheduler : SchedulerType
init(serialQueue: DispatchQueue, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
self.configuration = DispatchQueueConfiguration(queue: serialQueue, leeway: leeway)
}
区别较大的是SerialDispatchQueueScheduler.schedule方法被改为调用自身的scheduleInternal方法了。默认情况下,还是会调用DispatchQueueConfiguration.schedule方法。根据上述介绍的,默认就会将传入的闭包抛到队列异步执行。
这里预告一下,MainScheduler就会重写scheduleInternal来适配主线程的逻辑。
// SerialDispatchQueueScheduler.swift
public final func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
return self.scheduleInternal(state, action: action)
}
func scheduleInternal<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
return self.configuration.schedule(state, action: action)
}
源码:
MainScheduler
上述提到,MainScheduler继承自SerialDispatchQueueScheduler,因为主线程为单线程,这里可以理解MainScheduler为一个特殊的SerialDispatchQueueScheduler,即特殊的串行队列。
下述代码中,MainScheduler拥有一个成员变量_mainQueue,即DispatchQueue.main。numberEnqueued是用于处理多线程并发的,这个后面会讲到。
// MainScheduler.swift
public final class MainScheduler : SerialDispatchQueueScheduler {
private let _mainQueue: DispatchQueue
let numberEnqueued = AtomicInt(0)
/// Initializes new instance of `MainScheduler`.
public init() {
self._mainQueue = DispatchQueue.main
super.init(serialQueue: self._mainQueue)
}
...
值得一提的是,MainScheduler是一个单例,并且提供了两个实体共我们使用。
- instance即使用MainScheduler本身。
- asyncInstance即利用
DispatchQueue.main创建一个SerialDispatchQueueScheduler对象。后续的逻辑就与上述SerialDispatchQueueScheduler的介绍相同了。
// MainScheduler.swift
public static let instance = MainScheduler()
public static let asyncInstance = SerialDispatchQueueScheduler(serialQueue: DispatchQueue.main)
在MainScheduler中重写了scheduleInternal方法。最终的结果是将传入的闭包抛到主线程异步执行。当然,若当前为主线程且没有正在执行的逻辑(previousNumberEnqueued == 0),会采用同步执行的方式。
在这里有一个numberEnqueued属性+1、-1的逻辑。上文可知,numberEnqueued是一个原子属性的Int型(AtomicInt,RxSwift自己封装的线程安全Int型)。在执行闭包前+1,执行闭包后-1。此操作笔者个人理解是在多线程并发调用的情况下,保证事件的顺序不被修改,而又能够在主线程空闲的情况下同步执行。这里的+1、-1也有一点像递归锁的思想。
// MainScheduler.swift
override func scheduleInternal<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
let previousNumberEnqueued = increment(self.numberEnqueued)
if DispatchQueue.isMain && previousNumberEnqueued == 0 {
let disposable = action(state)
decrement(self.numberEnqueued)
return disposable
}
let cancel = SingleAssignmentDisposable()
self._mainQueue.async {
if !cancel.isDisposed {
_ = action(state)
}
decrement(self.numberEnqueued)
}
return cancel
}
源码:
ConcurrentMainScheduler
在MainScheduler.swift的文件开头有这样一段注释:
This scheduler is optimized for
observeOnoperator. To ensure observable sequence is subscribed on main thread usingsubscribeOnoperator please useConcurrentMainSchedulerbecause it is more optimized for that purpose.
大体意思是MainScheduler针对observeOn操作符进行了优化,而ConcurrentMainScheduler针对subscribeOn进行了优化。所以接下来,我们看看ConcurrentMainScheduler究竟是啥。
ConcurrentMainScheduler可以理解为是MainScheduler的一个装饰器,因为除了重写SchedulerType.schedule()方法,其他逻辑都是调用MainScheduler对应的方法。
// ConcurrentMainScheduler.swift
public final class ConcurrentMainScheduler : SchedulerType {
public typealias TimeInterval = Foundation.TimeInterval
public typealias Time = Date
private let _mainScheduler: MainScheduler
private let _mainQueue: DispatchQueue
/// - returns: Current time.
public var now: Date {
return self._mainScheduler.now as Date
}
private init(mainScheduler: MainScheduler) {
self._mainQueue = DispatchQueue.main
self._mainScheduler = mainScheduler
}
/// Singleton instance of `ConcurrentMainScheduler`
public static let instance = ConcurrentMainScheduler(mainScheduler: MainScheduler.instance)
...
在schedule方法中与MainScheduler.scheduleInternal的区别在于,少了原子属性作为线程安全的处理。结合上述作者推荐在subscribeOn操作符使用,笔者个人理解为订阅的过程不存在并发的情况,所以可以忽略线程安全的问题。
// ConcurrentMainScheduler.swift
public func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
if DispatchQueue.isMain {
return action(state)
}
let cancel = SingleAssignmentDisposable()
self._mainQueue.async {
if cancel.isDisposed {
return
}
cancel.setDisposable(action(state))
}
return cancel
}
源码:
RecursiveImmediateScheduler
RecursiveImmediateScheduler不是一个Scheduler,这里我们先简单回顾一下observeOn。
在创建observeOn时,代码会根据传入的scheduler是否为SerialDispatchQueueScheduler来创建ObserveOnSerialDispatchQueue对象或ObserveOn对象。
// ObserveOn.swift - extension ObservableType
public func observeOn(_ scheduler: ImmediateSchedulerType) -> Observable<Element> {
if let scheduler = scheduler as? SerialDispatchQueueScheduler {
return ObserveOnSerialDispatchQueue(source: self.asObservable(), scheduler: scheduler)
}
else {
return ObserveOn(source: self.asObservable(), scheduler: scheduler)
}
}
- 若为ObserveOnSerialDispatchQueue,最终会在事件传递时触发
ObserveOnSerialDispatchQueueSink.onCore方法,对应的就是我们上述所讲的schedule方法。
// ObserveOn.swift - class ObserveOnSerialDispatchQueueSink
override func onCore(_ event: Event<Element>) {
_ = self.scheduler.schedule((self, event), action: self.cachedScheduleLambda!)
}
- 若为ObserveOn,最终会在事件传递时触发
ObserveOnSink.onCore方法,这里调用了一个我们没见过的方法scheduleRecursive递归的线程调度。
// ObserveOn.swift - class ObserveOnSink
override func onCore(_ event: Event<Element>) {
let shouldStart = self._lock.calculateLocked { () -> Bool in
self._queue.enqueue(event)
switch self._state {
case .stopped:
self._state = .running
return true
case .running:
return false
}
}
if shouldStart {
self._scheduleDisposable.disposable = self._scheduler.scheduleRecursive((), action: self.run)
}
}
scheduleRecursive来源于ImmediateSchedulerType的扩展。这里会根据需要执行的闭包以及scheduler对象创建一个RecursiveImmediateScheduler对象。这里也可以理解是RecursiveImmediateScheduler为我们设置的Scheduler对象的装饰器。
// ImmediateSchedulerType.swift - extension ImmediateSchedulerType
public func scheduleRecursive<State>(_ state: State, action: @escaping (_ state: State, _ recurse: (State) -> Void) -> Void) -> Disposable {
let recursiveScheduler = RecursiveImmediateScheduler(action: action, scheduler: self)
recursiveScheduler.schedule(state)
return Disposables.create(with: recursiveScheduler.dispose)
}
RecursiveImmediateScheduler.schedule方法简单来说就是调用我们设置的Scheduler.schedule方法进行线程切换,值得注意的是,执行的闭包中会以RecursiveImmediateScheduler.schedule方法作为闭包参数。
// RecursiveScheduler.swift - class RecursiveImmediateScheduler
func schedule(_ state: State) {
var scheduleState: ScheduleState = .initial
let d = self._scheduler.schedule(state) { state -> Disposable in
// best effort
if self._group.isDisposed {
return Disposables.create()
}
let action = self._lock.calculateLocked { () -> Action? in
...
return self._action
}
if let action = action {
action(state, self.schedule)
}
return Disposables.create()
}
...
}
不妨再结合ObserveOnSink.onCore方法中调用scheduleRecursive时传入的run方法。方法的最后会再次调用闭包recurse即为上述提到的RecursiveImmediateScheduler.schedule方法。以此达到递归调用的效果,线程对应的时scheduler所对应的线程。递归的出口为下述的布尔型shouldContinue。
// ObserveOn.swift - class ObserveOnSink
func run(_ state: (), _ recurse: (()) -> Void) {
let (nextEvent, observer) = self._lock.calculateLocked { () -> (Event<Element>?, Observer) in
if !self._queue.isEmpty {
return (self._queue.dequeue(), self._observer)
}
else {
self._state = .stopped
return (nil, self._observer)
}
}
if let nextEvent = nextEvent, !self._cancel.isDisposed {
observer.on(nextEvent)
if nextEvent.isStopEvent {
self.dispose()
}
}
else {
return
}
let shouldContinue = self._shouldContinue_synchronized()
if shouldContinue {
recurse(())
}
}
这里引申出了一个问题:为什么ObserveOnSerialDispatchQueue的最后可以直接调用scheduler.schedule方法,而ObserveOn却需要将事件先缓存,然后递归调用呢?
-
回想一下
observeOn方法中需要判断scheduler是否为SerialDispatchQueueScheduler或其子类。这说明如果执行的DispatchQueue为串行队列,那就不存在线程并发的问题。故ObserveOnSerialDispatchQueue不需要这种缓存操作的处理。 -
而若是创建
ObserveOn则意味着设置的scheduler有线程并发的能力(譬如ConcurrentDispatchQueueScheduler)。利用队列集合先将事件缓存,后续通过递归的方式传递给下游,则相当于是一种并发的数据合流的效果,这样可以保证数据按其相对顺序往下传递。
// ObserveOn.swift - extension ObservableType
public func observeOn(_ scheduler: ImmediateSchedulerType) -> Observable<Element> {
if let scheduler = scheduler as? SerialDispatchQueueScheduler {
return ObserveOnSerialDispatchQueue(source: self.asObservable(), scheduler: scheduler)
}
else {
return ObserveOn(source: self.asObservable(), scheduler: scheduler)
}
}
源码:
最后
本文重点解析了常用的ConcurrentDispatchQueueScheduler、MainScheduler及它们相关类的源码。因为里面用到的都是GCD相关的逻辑,代码的逻辑还是比较好理解的。
系列文章: