携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
本篇文章主要是讲解协程相关的知识点,并以
delay()
方法简单分析协程如何实现的挂起以及恢复
协程实现的原理就是CPS+状态机
,掘金很多优秀的文章都都写的很好,我也写不出太过于原理性的文章。本篇文章以一个挂起函数delay()
举例,来简单讲解协程如何实现的挂起与恢复,以及为什么delay()
不会阻塞主线程。
delay()
的挂起与恢复
我们以下面的代码举例:
lifecycleScope.launch {
delay(1000)
val a = 10
val b = 50
println(a + b)
}
kotlin编译器会将上面的代码翻译下面代码(非常简陋的伪代码):
private fun test(completion: Continuation<Unit>) {
class StateMachine(completion) {
val tag = 0
override fun resumeWith() {
return test(this)
}
}
val machine = completion as? StateMachine ?: StateMachine(completion)
when (machine.tag) {
0 -> {
//1.
//当delay方法执行完毕,就会调用machine的resumeWith()方法恢复协程执行
machine.tag = 1
delay(1000, machine)
}
1 -> {
//2.
val a = 10
val b = 50
println(a + b)
}
}
}
挂起
创建一个状态机对象StateMachine
,第一次状态机tag等于0,就会执行协程的挂起方法delay
,并传入状态机实例machine
,当delay
方法执行完毕后,就会调用传入的machine
的resumeWith
方法恢复之后代码的执行。
delay
方法是如何执行的呢?为什么不会阻塞主线程?我们探究其实现原理:
public suspend fun delay(timeMillis: Long) {
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
继续看下HandlerContext
中scheduleResumeAfterDelay
方法(这里以HandlerContext
类举例):
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
val block = Runnable {
with(continuation) { resumeUndispatched(Unit) }
}
handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))
}
可以看到,这就是借助了Handler
发送了一个延时执行的Message
消息,所以调用delay()
方法自然不会阻塞主线程
的执行了。
恢复
重点scheduleResumeAfterDelay
这段代码:
val block = Runnable {
with(continuation) { resumeUndispatched(Unit) }
}
当上面的Message
被执行后,就会调用传入的continuation恢复协程的执行
,可以把这个continuation
理解为我们上面创建的machine
对象,调用的方法resumeUndispatched
是为resumeWith()
方法。
当协程的挂起方法delay
执行完毕后,就会走到状态机StateMachine
的方法resumeWith
中,此时的tag已经变为1
了,所以当再次调用test(Continuation)
方法,并且走when
的第二个分支,执行下面的代码块逻辑:
1 -> {
//2.
val a = 10
val b = 50
println(a + b)
}
`
笔者这里主要是简单的描述了下
协程的挂起与恢复
,伪代码写的也非常简陋,主要是方便大家理解,真实的情况请大家参考协程官方的源码,有什么疑问可以评论区一起交流。
总结
本篇文章主要是以delay()
方法作为一个入口,看下协程如何挂起与恢复的,并没有多么神秘,接下来准备继续写一系列协程相关的文章,如果您感觉文章写的还行,可以给个赞支持下,感谢!!
参考文章
这是我看过的讲解协程的最好的一篇文章,大概有几万字,我看了大概5-6个小时,非常值得大家细细品读。