Kotlin-挂起函数挂起了啥?
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 2 天,点击查看活动详情
不瞒各位,笔者在刚开始学习 Kotlin 协程的时候看到挂起函数总是想到阻塞线程,为啥呀?因为从表面上看,执行挂起函数的时候,后面的代码就不执行了,并且大部分挂起函数都是耗时的,这与在线程中执行耗时操作会阻塞线程类似,举个栗子:
1 | GlobalScope.launch { |
从 ① 和 ② 的输出结果来看,代码的确间隔 1 秒钟执行,从代码顺序上看,delay(1000)
怎么看都是阻塞了线程 1 秒钟,类似 Java 中的 Thread.sleep(1000)
,这看起来不就是阻塞执行了么?
看到这里,相信不少读者已经迷糊了,Kotlin 协程不是非阻塞的么?上面代码看起来是阻塞的啊。
何为阻塞
欸,别慌,先搞明白阻塞是什么,阻塞的对象是谁?
阻塞对比现实世界的例子比较好理解,比如去超市购物结账时:
- 你结账的窗口扫码枪坏了,结不了账(发生阻塞)
- 等到换个新的扫码枪才能结账(阻塞恢复)
现在看看阻塞的是谁呢?阻塞的就是这个结账窗口(线程),没错,阻塞的是线程。
阻塞不阻塞是针对当前线程来说的,去别的线程执行,自然不会阻塞当前线程,比如上述结账,你可以去其他窗口进行结账。
回过头看上面的代码例子,无论是从代码执行顺序还是代码执行结果上看,都像是阻塞执行,我们看看下面这个例子:
1 | fun test() { |
Android 开发同学看见 Handler 应该很熟悉了,对比这两个代码示例,从代码执行顺序还是代码执行结果上是否是一样的呢?或许我们应该看看 delay
的源码了
1 | public suspend fun delay(timeMillis: Long) { |
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
这个操作,和 handler.postDelay()
类似,本质上是设置一个延时的回调,时间到了就调用 cont
的 resume 等方法让协程恢复。
Kotlin 协程的非阻塞体现在哪呢?
上面我们已经知道阻塞的对象是线程,线程是在 CPU 里执行的,所以可以说阻塞的对象也是 CPU,而 Kotlin 协程的非阻塞只是语言层面的,不是操作系统层面的阻塞,说明在协程中调用阻塞的方法,比如 Thread.sleep()
的时候,协程仍然是阻塞的。
Kotlin 协程的非阻塞是与线程阻塞相比较的,比如我们调用 Thread.sleep()
休眠线程达到延迟的效果,协程则可以使用 delay()
挂起同样达到延迟效果。
那挂起函数挂起了啥?
首先我们需要知道,协程在哪里挂起的,针对第一个代码示例,可以看出来,协程在 delay(1000)
处挂起,那它挂起了什么?
我们不妨想一下,在酒店点菜时的挂起,客人点了一份菜单,但是告知现在不能开始做,等半小时后再开始做,这时候我们可以在系统中先录入这份菜单,点击挂起,输入挂起时长保存,等挂起时长到了,系统自动把菜单发给后厨,后厨开始做菜。
上面酒店点菜的示例,和 delay()
的效果类似,都是延迟触发后续的逻辑。在点菜示例中挂起的什么?是菜单还是做菜动作?这里笔者认为是做菜的动作,而菜单认为是挂起点,即从菜单挂起,也从菜单恢复,对应第一个代码示例,既从 delay(1000)
处挂起,也从 delay(1000)
处恢复,挂起的是 delay(1000)
之后要执行的代码,或者说挂起了协程恢复后要执行的代码,即 ② 处代码。
现在还有 suspendCancellableCoroutine
比较神秘,我们看下它的源码:
1 | public suspend inline fun <T> suspendCancellableCoroutine( |
suspendCoroutineUninterceptedOrReturn
这个方法的源码是看不到的,它本身就没有源码:
1 | public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T { |
它的作用就是帮我们拿到当前协程的 Continuation
实例,不过我通过翻阅 Kotlin 源码(搜索 suspendCoroutineUninterceptedOrReturn
关键字)找到了以下代码,笔者认为下图[1]中代码就是具体的实现,代码中的注释,正好对应源码中的 block
、Continuation
:
总结
Kotlin 协程的挂起函数挂起了啥,可以理解为挂起的是挂起函数恢复之后要执行的逻辑。
说明
以上为笔者个人见解,仁者见仁,智者见智,大家可以求同存异,同时笔者水平有限,如有不同见解,欢迎交流。