关于Kotlin协程执行顺序不明确及代码输出不可预测的原因咨询
嗨,我来帮你拆解一下这个问题,你碰到的其实就是协程并发调度带来的正常现象,并非什么奇怪的bug哦。
先理清楚你的代码逻辑:你启动了三个独立的协程job1、job2、job3,它们都做了1秒延迟后打印"World!"。主线程(也就是main函数所在的协程)先打印"Hello,"和job1的活跃状态,然后调用job1.join()等待它完成,之后再打印job1的完成状态,最后依次等待job2和job3完成。
你预期job1完成后立刻打印它的完成状态,再看到另外两个"World!",但实际却是三个"World!"都挤在了job1完成状态打印的前面,这背后有两个核心原因:
协程的并发调度特性:三个协程几乎是同时启动的,它们的
delay(1000L)也几乎同步开始计时。1秒后,这三个协程的打印任务会被协程调度器(默认是Dispatchers.Default,基于线程池实现)同时唤醒,随后它们会争抢线程资源来执行打印操作。这时候三个"World!"的输出顺序本身就是不确定的——谁先抢到线程谁先打印,这属于正常的并发竞争场景。join()仅等待目标协程,不阻塞其他协程:当你调用job1.join()时,主线程会挂起,直到job1完成。但job2和job3此时已经处于运行状态,它们的delay也接近结束。当job1完成、主线程恢复准备打印job1的完成状态时,job2和job3的打印任务可能已经被调度器安排执行了,所以它们的"World!"会先于主线程的打印操作输出。
举个生活化的例子:就像你让三个人同时开始计时,1秒后喊"World",你自己先等第一个人喊完,然后准备记录他完成了,但另外两个人可能和第一个人几乎同时喊出声,所以你还没来得及记录,就先听到了另外两个人的声音。
如果想达到你预期的输出效果,只要把job2和job3的启动放在job1.join()之后就行,这样它们会在job1完成后才开始执行,自然就会在job1的完成状态打印之后输出:
suspend fun main() { val job1 = GlobalScope.launch { delay(1000L) println("World!") } println("Hello,") println(job1) job1.join() println(job1) // 等job1完成后再启动另外两个协程 val job2 = GlobalScope.launch { delay(1000L) println("World!") } val job3 = GlobalScope.launch { delay(1000L) println("World!") } job2.join() job3.join() }
内容来源于stack exchange

