RxSwift之并发序列实现原理

RxSwift的对于序列的调度,在核心探究的那一章并没有明确的介绍,只是带过,所以另开一章来介绍。

首先来看问题,这是RxSwift的一个并发执行的序列

上图中有100个elements,都会在子线程执行。

问题一:线程是怎么创建的?

问题二:100个element会依次打印,怎么确保顺序?

带着问题去探究,这样会更好的理解其中的原理。

第一个问题,线程的创建,在subscrbe订阅之前,有一个特殊的处理

.subscribe(on: ConcurrentDispatchQueueScheduler.init(qos: .background))

这里subscribe(on:)传入了一个ConcurrentDispatchQueueScheduler类型的参数

在这个方法我们可以明确的看到,这里返回了一个新的序列,而这个序列就是SubscribeOn类型的一个序列,有了这个概念之后,之后的subscribe方法订阅的则是SubscribeOn这个中间序列这个就很好理解了。

重点是这里传入的参数ConcurrentDispatchQueueScheduler。所以我们优先来看一下这个参数的内容是什么

在这里可以明确的看到,这是一个便利构造方法,这里调用自己的init方法,需要的参数则为一个DispatchQoS类型的参数,这里顺带说一下DispatchQoS。

DispatchQoS 为调度优先级 五种类型

static let userInteractive: DispatchQoS

用户交互任务的服务质量类,例如动画、事件处理或应用程序用户界面的更新。

static let userInitiated: DispatchQoS

阻止用户主动使用您的应用程序的任务的服务质量等级。

static let `default`: DispatchQoS

默认的服务质量等级。

static let utility: DispatchQoS

用户不主动跟踪的任务的服务质量等级。

static let background: DispatchQoS

您创建的维护或清理任务的服务质量等级。

static let unspecified: DispatchQoS

缺乏服务质量等级。

上图中的调用自己的init的方法时候,需要一个DispatchQueue队列的参数,所以在这里使用传入的参数DispatchQoS 来创建一个DispatchQueue队列,这里使用的也是一个便利构造方法

init(label: String, qos: DispatchQoS, attributes: DispatchQueue.Attributes, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency, target: DispatchQueue?)

这里的DispatchQueue.Attributes 如果为空,则为串行队列

这里的意思就为创建一个名称为rxswift.queue.(qos名称)的调度队列当作参数传给ConcurrentDispatchQueueScheduler的init方法,后面的另外一个参数leeway为间隔的时间,这里默认值为0纳秒,通过init方法,保存configuration这个属性。configuration 又是什么呢?


这是一个保存了队列和间隔时间的结构体,所以在这一步就是创建了一个队列和间隔时间的参数放在了SubscribeOn这个序列中。

现在的话,中间序列在创建的时候就获取到了ConcurrentDispatchQueueScheduler类型的对象,ConcurrentDispatchQueueScheduler的对象保存了属性configuration,类型为DispatchQueue结构体,DispatchQueue结构体中保存了我们创建好的DispatchQueue队列。

现在知道了参数,看看回头看我们的subscribe(on:)方法创建中间序列的时候做了什么。在第一张图中看到返回的是SubscribeOn类型的序列,所以直接来到SubscribeOn中

这里是不是很熟悉,继承自pruduce,保存source和调度者,和普通的序列做的几乎相同的事情。

那么订阅呢,我们都知道订阅其实最后都是会走到SubscribeOn的run方法,所以直接来看run方法就好了,先看sink

在这里看到,其实是同样的逻辑,保存属性,然后走自己的父类sink的构造方法。这里需要注意的是SubscribeOnSink 拥有ObservableType和Observer两个协议,说明SubscribeOnSink是一个序列同时也是一个订阅者,这是一个很重要的概念。

再接着上一步sink之后的subscription,看到是调用了SubscribeOnSink的run方法。同样快速的来到SubscribeOnSink的run方法

这里和之前不同了哦,在这打个断点看看。


这里可以很明确的看到就是这一句在执行我们的发布订阅,想想我们的第二个问题,在这里是不是看它的实现就可以知道了。所以准备好了吗。

嗯?可是去哪里找呢,来看下图 

先看两个箭头指的地方,observable 和匿名类 AnonymousObserver,这里是不是也同样证明了SubscribeOnSink是一个序列同时也是一个订阅者。

lldb调试之后翻译self.parent.source.subscribe(self)这一句

SubscribeOnSink.SubscribeOn.ObservableSequence.subscribe(SubscribeOnSink)

所以去看ObservableSequence的subscribe方法就好

看到继承自producer,其实这里我们就可以去看run方法了。因为明确的知道ObservableSequence没有重写subscribe方法的话,那必然还是会回到ObservableSequence的run方法,这里其实需要关注的是elements和scheduler这两个属性。

我们看到在创建sink的时候将ObservableSequence传入,那就说明ObservableSequenceSink是拥有所有的发布订阅和一个调度者对象的。再来看看ObservableSequenceSink的run方法做了什么

这里看其实还是有一点不明确的,但是这里有看出一点端倪,就是iterator,这是迭代器的意思,然后recurse递归,根据方法名其实能猜到个大概,就是在next的时候,根据迭代器递归的去发布订阅。

那么这里的sceduler又是谁呢,这里其实就是我们在创建ObservableSequence传入的CurrentThreadScheduler。

再来看内部的实现scheduleRecursive方法,scheduleRecursive是来自ImmediateSchedulerType协议的扩展

这里是创建RecursiveImmediateScheduler 的类,根据传入的参数state调用schedule方法,state也是ObservableSequence 保存的属性elements。等等,这里准确的来说是通过Sequence类型的makeIterator()方法获得自己的迭代器的属性。而action就是我们逃逸闭包中的代码。

那么很明确,在这里是调用schedule方法完成的实现,再进一步

重点的就一句代码,如果action存在就递归的调用action方法,而action呢,就是我们传入的逃逸闭包,这里action的创建也很有意思,在加锁的情况下根据迭代器来一个一个的创建逃逸闭包,所以这里也就是我们问题二的答案,通过加锁来控制迭代器创建发布订阅的逃逸闭包,这样达到一个顺序的目的。

调试的时候也可以明确的看到这一点。

总结

RxSwift的并发序列是:

1.先创建一个带有队列参数的中间序列。

2.中间序列既可以作为序列被订阅,也可以订阅源序列。

3.通过递归的方式在逃逸闭包中发布订阅

4.通过加锁和迭代器的方式来控制逃逸闭包的创建,实现顺序的输出源序列订阅的发布。

你可能感兴趣的:(RxSwift之并发序列实现原理)