RxSwift的subscribeOn与observeOn源码解析

1,097 阅读11分钟

前言

上篇文章梳理了整个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源码解析。

系列文章: