正规白帽SEO,让排名更快速、客户更易找

15811308047

当前位置: 首页 >> 新闻中心 >> 行业动态

追剧软件无广告再谈协程:Sequence的巧妙实现

发布时间:2023-01-29 08:52:21

前言之前多少写过一些协程的文章一直自认为协程这块理解的还是比较到位,但是最近被团队里的同学各种奇怪问题,属实给整不会了所以这两天又回过头看了看协程今天的这篇文章就是解答团队了同学的问题:Sequenc网络营销公司e是怎么做到“冷流”的效果。

正文正式开始前,先补充一些前置知识冷流:只有在有消费的时候,才会执行生产Kotlin协程的本质:状态机 + 回调以下内容请配合上述的知识点服用1、简单的用法咱们先看一段简单网络营销公司的使用,以及对应的Log效果fun

main() { val seq = sequence { println("yield(1)开始执行") yield网络营销公司(1) println(

"yield(1)执行完毕") println("yield(2)开始执行") yield(2) println网络营销公司("yield(2)执行完毕") println(

"yield(3)开始执行") yield(3) println("yield(3)执行完毕") 网络营销公司 } seq.forEach { println(

"forEach -> $it") } } 日志如下:yie网络营销公司ld(1)开始执行 forEach -> 1yield(1)执行完毕 yield(2)开始执行 forEach -> 2yield(2

)执行完毕 网络营销公司 yield(3)开始执行 forEach -> 3yield(3)执行完毕 可以看到,yield函数执行完,没有继续执行,而是“跳”去执行了forEach而有网络营销公司意思的是forEach执行完,没有继续循环,而是代码逻辑“跳”到了。

yield后面的代码无论是forEach也好还是yield,“看起来”都是“同步函数”,为什么会反复横跳?今天咱们就是来聊为什么简单网络营销公司揭秘:这里的yield是suspend函数;这里的forEach封装了

iterator2、Sequence.forEach我们来看一眼Sequence.forEach的实现:publicinlinef网络营销公司un Sequence.forEach(action: (

T) -> Unit): Unit { for (element inthis) action(element) 网络营销公司 } 反编译一下,可以直观地看到这里是基于iterator的封装:public static

final void forEach(@NotNull Sequence $this$fo网络营销公司rEach, @NotNull Function1 action) { // 省略部分代码 Iterator var4 = $

this$forEach.iterator网络营销公司(); while(var4.hasNext()) { Object element = var4.next(); action.invoke(ele网络营销公司ment); } }

记住这里的实现,一会有惊喜3、suspend fun yield(value: T)再瞅一眼yield,就会发现这里直接启用了suspend,也就是网络营销公司借助了协程的能力suspend + Iterator,我猜到这应该就有同学能够意识到Sequence是如何实现的了。

这里咱们停留一分钟,大家可以思考设计一下方案4、源码阅读先顺着入口函数看一下实现:p网络营销公司ublic fun sequence(@BuilderInference block: suspend SequenceScope

.() -> Unit): Sequence = Sequence {网络营销公司 iterator(block) } public inline fun Sequence(crossinline iterator: () -> Iterator

): Sequen网络营销公司ce = object : Sequence { override fun iterator(): Iterator = iterator() } p网络营销公司ublic fun

iterator(@BuilderInference block: suspend SequenceScope.() -> Unit): Iterator { val网络营销公司 iterator = SequenceBuilderIterator

() iterator.nextStep = block.createCoroutineUnintercepted网络营销公司(receiver = iterator, completion = iterator) return iterator }

没有特别复杂的逻辑,核心在于Sequence网络营销公司BuilderIterator的实现点进去看一下:privateclassSequenceBuilderIterator : SequenceScope。

(), Iterator, Continuat网络营销公司ion { // 省略部分代码privatevar state = State_NotReady privatevar

nextValue: T? = nullvar n网络营销公司extStep: Continuation? = nulloverridefunhasNext(): Boolean {

// 具体分析见 4.2 处分析 } over网络营销公司ridefunnext(): T { // 具体分析见 4.3 处分析 } overridesuspend

funyield(value: T) { 网络营销公司 // 具体分析见 4.1 处分析 } overridefunresumeWith(result: Result) { result.ge网络营销公司tOrThrow() state = State_Done } }

接下来我们根据demo中代码逐行分析一下,SequenceBuilderIterato网络营销公司r的逻辑val seq = sequence { println("yield(1)开始执行") yield(

1) // ... } 网络营销公司 代码按正常顺序执行到yield(1):4.1、yeild()overridesuspendfunyield(value: T) { nextValue = value 网络营销公司 state = State_Ready

return suspendCoroutineUninterceptedOrReturn { c -> nextStep = c 网络营销公司 COROUTINE_SUSPENDED } }

代码走到yield里,很直白:nextValue = value state = State网络营销公司_Ready 保存入参value变量,然后置为State_Ready状态此外最重要的则是COROUTINE_SUSPENDED。

,执行到这就意味着suspend yield被挂起,直到网络营销公司resume被调用才会恢复并执行后续的代码逻辑到这冷流的效果就出现了,只要没有代码执行resume,那么yield(1)就不会往后执行所以接下来咱们要看哪里的源码应该不言而喻吧?。

冷流的执行,需要末端网络营销公司操作符也就是咱们demo中的forEach4.2、hasNext()上文咱们看了forEach的实现,所以我们直接定位到hasNext()即可overridefunhasNext()

: Boolean网络营销公司 { while (true) { when (state) { State_NotReady -> {} // 省略部分代码

State网络营销公司_Done -> returnfalse State_Ready -> returntrueelse -> throw exceptionalState() } 网络营销公司 state = State_Failed

val step = nextStep!! nextStep = null step.resume(Unit) 网络营销公司 } } 从hasNext的实现可以看到这是一个状态机的逻辑转化。

当state == State_NotReady则不会return,并且执行step.res网络营销公司ume(Unit)根据demo执行的顺序,目前的state == State_Ready,所以会返回true,因此接下来就会执行到。

next中4.3、next()overridefunnext(): 网络营销公司T { when (state) { State_NotReady -> return nextNotReady() 。

// 省略部分代码 网络营销公司 State_Ready -> { state = State_NotReady val result = nextValue

as T nextVal网络营销公司ue = nullreturn result } else -> throw exceptionalState() } }

这里没啥好说的网络营销公司,切换state = State_NotReady,把nextValue返回出去也就执行到了demo中的:seq.forEach { println("forEach -> $it。

"网络营销公司) } 执行结束后,逻辑就再次走到了hasNext中此时状态已经是State_NotReady,所以逻辑便走到了step.resume(Unit)到此yeild的挂起就网络营销公司被恢复,demo中的代码就继续执行:。

println("yield(1)执行完毕") println("yield(2)开始执行") yield(2) 网络营销公司逻辑到此已经全部续上了,后续的代码就不需要再多解释了吧…尾声关于协程的技术点还有很多很多。

有机会再继续展开吧~


上一篇:免费加速器永久免费版PMC函数学习之SEQUENCE 函数-04 下一篇:(永久免费)加速器下载Excel函数Sequence用不了?试试普通数组公式代替它,一样好用!