前言
上篇文章梳理了整个RxJava/RxSwift的定义、订阅、事件产生-->消费的过程与源码解析,本文主要是想通过前文总结的思路聊聊RxSwift的subscribeOn与observeOn的源码实现,如果没看过前文的建议先移步阅读。(ps:本文Rx源码基于RxSwift 5.1.1)。如果是看过之前总结的RxJava版本可能内容有雷同。
流程
基于前文所解析的Observable.create,我们新增subscribeOn与observeOn两个操作符。随码附赠一个流程图,主要是补充前文解析的代码数据流程加上本次我们聊到的subscribeOn与observeOn。
Observable<String>.create { (ob) -> Disposable in
print("事件产生线程:\(Thread.current.description)")
ob.onNext("I'm ")
ob.onCompleted()
return Disposables.create()
}
.subscribeOn(ConcurrentDispatchQueueScheduler.init(qos: .userInteractive))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { (str) in
print("事件消费线程:\(Thread.current.description)")
print(str)
}, onError: nil, onCompleted: nil, onDisposed: nil)
从前文的代码解析可知,RxSwift在订阅期间依赖的是抽象方法subscribe的实现。一般继承Producer的类则是需要实现run方法。事件下发依赖的则是onNext/onError等方法的实现。故下面的文章也会围绕这两方面来总结subscribeOn与observeOn的实现。
subscribeOn
外层调用subscribeOn方法,新建一个SubscribeOn对象,以自身作为SubscribeOn的source。
// SubscribeOn.swift
public func subscribeOn(_ scheduler: ImmediateSchedulerType)
-> Observable<Element> {
return SubscribeOn(source: self, scheduler: scheduler)
}
...
final private class SubscribeOn<Ob: ObservableType>: Producer<Ob.Element> {
let source: Ob
let scheduler: ImmediateSchedulerType
init(source: Ob, scheduler: ImmediateSchedulerType) {
self.source = source
self.scheduler = scheduler
}
...
源码:
run
由于SubscribeOn继承自Producer类,这里我们直接看它的run方法即可。
- 创建SubscribeOnSink对象,用于装饰传入的observer(上一级的观察者Observer)。同时调用SubscribeOnSink的run方法。
- 在SubscribeOnSink.run方法中,self.parent.scheduler.schedule即为触发线程调度的代码。这里的parent是创建时传入的SubscribeOn对象,source即为SubscribeOn的source(上一级)。
- 闭包的内容为线程切换后执行代码,这里即为调用上一级的订阅逻辑。也就是订阅在切换线程后继续往上传递了。
// SubscribeOn.swift
override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Ob.Element {
let sink = SubscribeOnSink(parent: self, observer: observer, cancel: cancel)
let subscription = sink.run()
return (sink: sink, subscription: subscription)
}
// SubscribeOn.swift
final private class SubscribeOnSink<Ob: ObservableType, Observer: ObserverType>: Sink<Observer>, ObserverType where Ob.Element == Observer.Element {
typealias Element = Observer.Element
typealias Parent = SubscribeOn<Ob>
...
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
}
源码:
onNext
参照开头的流程图,事件流产生通过onNext往下传递就是通过SubscribeOnSink的onNext方法。前文也有提到,在RxSwift中大多是重写ObserverType协议的func on(_ event: Event)方法。
on方法调用的是其父类Sink的forwardOn,这里只是调用了起成员变量_observer(就是在run方法中传入的observer,也就是其下一级的观察者)的on方法,并没有进行线程处理。
// SubscribeOn.swift - class SubscribeOnSink
func on(_ event: Event<Element>) {
self.forwardOn(event)
if event.isStopEvent {
self.dispose()
}
}
// Sink.swift - class Sink
final func forwardOn(_ event: Event<Observer.Element>) {
#if DEBUG
self._synchronizationTracker.register(synchronizationErrorMessage: .default)
defer { self._synchronizationTracker.unregister() }
#endif
if isFlagSet(self._disposed, 1) {
return
}
self._observer.on(event)
}
源码:
observeOn
外层调用observeOn方法,新建一个ObserveOn/ObserveOnSerialDispatchQueue对象,以自身作为ObserveOn/ObserveOnSerialDispatchQueue的source。这里会判断scheduler的类型,譬如MainScheduler就是继承SerialDispatchQueueScheduler类的。
接下来会分别看看ObserveOn和ObserveOnSerialDispatchQueue,区别主要在于onNext等事件传递时的线程安全问题。
// ObserveOn.swift
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)
}
}
// ObserveOn.swift
final private class ObserveOnSerialDispatchQueue<Element>: Producer<Element> {
let scheduler: SerialDispatchQueueScheduler
let source: Observable<Element>
init(source: Observable<Element>, scheduler: SerialDispatchQueueScheduler) {
self.scheduler = scheduler
self.source = source
...
}
...
// ObserveOn.swift
final private class ObserveOn<Element>: Producer<Element> {
let scheduler: ImmediateSchedulerType
let source: Observable<Element>
init(source: Observable<Element>, scheduler: ImmediateSchedulerType) {
self.scheduler = scheduler
self.source = source
...
}
...
源码:
run
由于ObserveOn/ObserveOnSerialDispatchQueue继承自Producer类,这里我们直接看它的run方法即可。
- ObserveOn.run方法中,创建包装类ObserveOnSink,调用source(ObserveOn创建时传入的source)的subscribe以ObserveOnSink对象为参数,将订阅往上传递。
- ObserveOnSerialDispatchQueue.run方法,创建包装类ObserveOnSerialDispatchQueueSink,调用source(ObserveOnSerialDispatchQueue创建时传入的source)的subscribe以ObserveOnSerialDispatchQueueSink对象为参数,将订阅往上传递。
订阅部分因为不涉及线程调度,所以两边的代码都是相似的。
// ObserveOn.swift
final private class ObserveOn<Element>: Producer<Element> {
let scheduler: ImmediateSchedulerType
let source: Observable<Element>
...
override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
let sink = ObserveOnSink(scheduler: self.scheduler, observer: observer, cancel: cancel)
let subscription = self.source.subscribe(sink)
return (sink: sink, subscription: subscription)
}
// ObserveOn.swift
final private class ObserveOnSerialDispatchQueue<Element>: Producer<Element> {
let scheduler: SerialDispatchQueueScheduler
let source: Observable<Element>
...
override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
let sink = ObserveOnSerialDispatchQueueSink(scheduler: self.scheduler, observer: observer, cancel: cancel)
let subscription = self.source.subscribe(sink)
return (sink: sink, subscription: subscription)
}
源码:
onNext
这里也是关注func on(_ event: Event)方法,由于ObserveOnSink与ObserveOnSerialDispatchQueueSink都是继承自ObserverBase类,该类对on方法进行了重写:
// ObserverBase.swift
class ObserverBase<Element> : Disposable, ObserverType {
private let _isStopped = AtomicInt(0)
func on(_ event: Event<Element>) {
switch event {
case .next:
if load(self._isStopped) == 0 {
self.onCore(event)
}
case .error, .completed:
if fetchOr(self._isStopped, 1) == 0 {
self.onCore(event)
}
}
}
func onCore(_ event: Event<Element>) {
rxAbstractMethod()
}
...
on方法最后会调用onCore方法,所以ObserveOnSink与ObserveOnSerialDispatchQueueSink重写的是onCore方法。
ObserveOnSink
1、先来看onCore方法:
- 最终是触发线程调度的逻辑:self._scheduler.scheduleRecursive((), action: self.run)
- 触发之前会有一个shouldStart的判断,该判断就是保证同一时间上述逻辑在并发情况下只被调用一次,保证线程安全。
- shouldStart的获取是一个加锁的逻辑,同步锁内的代码块有两个操作:1、将事件用队列_queue缓存起来(_queue是一个容量为10的队列,var _queue = Queue<Event<Element>>(capacity: 10));2、用全局变量_state判断是否已经在执行_scheduler的调度。
2、run方法在上述的onCore中被当作闭包调用_scheduler.scheduleRecursive,可以理解为当线程切换后执行的代码就是run方法:
- 刚开始有一个加锁闭包调用。若缓存队列不为空,会拿出在onCore时缓存的事件,以及成员变量_observer(ObserveOn.run方法中传入的observer,即其下一级的观察者),否则,则成员变量_state会更新状态.stop同时返回nil。
- 对应上述,队列不为空,事件不为空,则执行observer.on方法将事件往下传递。否则,返回。
- 最后的self._shouldContinue_synchronized()方法判断,其实就是判断队列是否为空。如果队列不为空这里会调用参数的闭包recurse(()),实际上这里是一个递归调用,可以理解为循环将self._queue队列内的事件消耗完。这里还涉及到RecursiveImmediateScheduler类的一些源码,因为本文不涉及scheduler相关的内容,有兴趣可以自行查看源码。
// 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)
}
}
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(())
}
}
func _shouldContinue_synchronized() -> Bool {
self._lock.lock(); defer { self._lock.unlock() } // {
if !self._queue.isEmpty {
return true
}
else {
self._state = .stopped
return false
}
// }
}
ObserveOnSerialDispatchQueueSink
1、onCore方法:
- 只是触发线程调度的逻辑。这里会传入闭包cachedScheduleLambda,切换线程后执行。cachedScheduleLambda的实例在构造方法中。
2、cachedScheduleLambda闭包:
- cachedScheduleLambda闭包的参数是一个(sink: ObserveOnSerialDispatchQueueSink<Observer>, event: Event<Element>)的元组,sink即ObserveOnSerialDispatchQueueSink对象,event即需要传递的事件。
- 闭包的这个pair参数(上述提到的元组类型),其实是在onCore的时候传入的。(self.scheduler.schedule((self, event), action: self.cachedScheduleLambda!))
- pair.sink.observer.on(pair.event)即调用ObserveOnSerialDispatchQueueSink的成员变量observer(ObserveOnSerialDispatchQueue.run方法中传入的observer,即其下一级的观察者)将事件往下传递。
ps:由于ObserveOnSerialDispatchQueue所接收的scheduler里面是一个串行队列,故不会出现多线程并发问题,不需要加锁。
// ObserveOn.swift - class ObserveOnSerialDispatchQueueSink
final private class ObserveOnSerialDispatchQueueSink<Observer: ObserverType>: ObserverBase<Observer.Element> {
...
var cachedScheduleLambda: (((sink: ObserveOnSerialDispatchQueueSink<Observer>, event: Event<Element>)) -> Disposable)!
init(scheduler: SerialDispatchQueueScheduler, observer: Observer, cancel: Cancelable) {
...
self.cachedScheduleLambda = { pair in
guard !cancel.isDisposed else { return Disposables.create() }
pair.sink.observer.on(pair.event)
if pair.event.isStopEvent {
pair.sink.dispose()
}
return Disposables.create()
}
}
override func onCore(_ event: Event<Element>) {
_ = self.scheduler.schedule((self, event), action: self.cachedScheduleLambda!)
}
源码:
总结
至此,subscribeOn与observeOn的源码就解析完了。需要理解的点是:
- subscribeOn的线程调度发生在subscribe/run方法,即订阅阶段。
- observeOn的线程调度则发生在onNext/onError(on方法)等事件加工/消费阶段。 接下来我们用上述提到的源码科学地分析来解决经常在Rx中遇到的线程问题。
常见问题
有时候看到一些教程中提到什么“事件产生的线程取决于subscribeOn”,“事件加工的线程默认与事件消费线程相同,受observeOn影响”等总结。这种常见问题就可以通过结合源码来解析了。
Rx默认会在哪个线程运行?
下列代码可以看到,代码的调用在main线程,最后的输出结果为都在main线程运行。 下列代码可以看到,代码的调用在main线程,最后的输出结果为都在main线程运行。
需要理解的是,Rx并不是默认在main线程,而是默认在当前线程。因为唯有subscribeOn与observeOn两个操作符具有线程调度的能力,准确地说是subscribeOn与observeOn中的scheduler拥有线程调度的能力,两个操作符只是在流程上做出控制而已。
所以说,Rx的代码在没有scheduler参与的情况下,没有切换线程,也就在当前线程运行了。
Observable<String>.create { (ob) -> Disposable in
print("事件产生线程:\(Thread.current.description)")
ob.onNext("I'm ")
ob.onCompleted()
return Disposables.create()
}
.map({ (str) -> String in
print("事件加工线程:\(Thread.current.description)")
return str + "rx"
})
.subscribe(onNext: { (str) in
print("事件消费线程:\(Thread.current.description)")
print(str)
}, onError: nil, onCompleted: nil, onDisposed: nil)
输出结果:
事件产生线程:main
事件加工线程:main
事件消费线程:main
判断在哪个线程执行问题
理解了线程切换只在subscribeOn或observeOn有切换线程的操作之后,我们再来看看如何判断每个代码块在什么线程执行的问题。这里还要结合上面总结的:1、subscribeOn的线程调度发生在subscribe/run,即订阅阶段。2、observeOn的线程调度则发生在onNext/onError(on方法)等事件加工/消费阶段。
下面可以看一个稍微复杂一点的例子,我们可以尝试直接通过这个代码判断出所在线程情况:
1、Observable<String>.create传入的事件产生的执行线程
Observable.create的例子中,一般事件产生都是发生在订阅之后,经过subscribeOn的run方法后会经历线程切换。故此代码块的调用在ConcurrentDispatchQueueScheduler所创建的线程。 (ps: 多数情况下,事件产生和订阅不是连续的,所以事件产生也不一定会发生在订阅的线程里。这可能是比较容易混淆的点。)
2、第一个map操作符执行线程
触发map操作符的逻辑会在onNext调用之后,而上一级onNext经过observeOn操作符,这里会经历一次线程切换,也就是说此代码块的调用将会在SerialDispatchQueueScheduler所创建的线程进行。
3、第二个map操作符执行线程
参照2的解析,代码块就会在MainScheduler(main)线程执行。
4、最终的事件消费执行线程
事件消费也是发生在onNext的事件流当中,这里可以寻找其往上最近的一个observeOn定义,因为后续的onNext都没有再经过observeOn操作符了,可以判断线程没有再次切换。故最终的事件消费也是在MainScheduler(main)线程。
Observable<String>.create { (ob) -> Disposable in
print("事件产生线程:\(Thread.current.description)")
ob.onNext("I'm ")
ob.onCompleted()
return Disposables.create()
}
.subscribeOn(ConcurrentDispatchQueueScheduler.init(qos: .userInteractive))
.observeOn(SerialDispatchQueueScheduler.init(qos: .userInteractive))
.map({ (str) -> String in
print("事件加工线程:\(Thread.current.description)")
return str + "rx"
})
.observeOn(MainScheduler.instance)
.map({ (str) -> String in
print("事件加工线程2:\(Thread.current.description)")
return str + "!"
})
.subscribe(onNext: { (str) in
print("事件消费线程:\(Thread.current.description)")
print(str)
}, onError: nil, onCompleted: nil, onDisposed: nil)
输出结果:
事件产生线程:number = 6 name = (null)
事件加工线程:number = 3 name = (null)
事件加工线程2:main
事件消费线程:main
总结:
通过上述的分析来总结一下:
- 订阅阶段是一个自下而上的过程,观察阶段(事件加工/事件消费)是一个自上而下的过程。
- 订阅线程的判断:因为订阅是自下而上的,线程切换的时机也是在subscribe/run方法,所以我们可以找从上往下的第一个subscribeOn操作符来判断线程。
- 观察线程的判断:自上而下的过程,可以找该代码块往上最近的一次observeOn。这也是很多教程提到的所谓map等操作符、事件消费线程observeOn影响的原因了。
subscribeOn的一次有效性问题
经常还会看到很多人说subscribeOn只有第一次设置的有效。那有效是不是代表着就只会切换这一次线程呢?其实上述的介绍已经有提到了,下面让我们用一个图示说明这个问题吧!(为了方便解析,先将事件加工忽略。)
Observable<String>.create { (ob) -> Disposable in
print("事件产生线程:\(Thread.current.description)")
ob.onNext("I'm ")
ob.onCompleted()
return Disposables.create()
}
.subscribeOn(ConcurrentDispatchQueueScheduler.init(qos: .userInteractive))
.subscribeOn(SerialDispatchQueueScheduler.init(qos: .userInteractive))
.subscribeOn(MainScheduler.instance)
.subscribe(onNext: { (str) in
print("事件消费线程:\(Thread.current.description)")
print(str)
}, onError: nil, onCompleted: nil, onDisposed: nil)
流程图画出的是在订阅过程中的数据流,可以看到每次调用run方法都会经历一次线程切换。即“第一次设置的有效”的意思并不是只会有一次线程切换,而是因为最终会切换到第一次设置的线程。
最后
本文解析了subscribeOn与observableOn操作符的源码,以及一些常见问题的解析。可以看到subscribeOn与observableOn操作符与线程调度有关,但它们的角色更多的是Rx上面的流程控制。所以我们完全可以将这两个操作符与线程调度scheduler独立开来。后面也会继续写文章总结RxSwift的scheduler源码解析。
系列文章: