前言
前面整理了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
observeOn
operator. To ensure observable sequence is subscribed on main thread usingsubscribeOn
operator please useConcurrentMainScheduler
because 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相关的逻辑,代码的逻辑还是比较好理解的。
系列文章: