为什么Kotlin中runBlocking会按顺序执行协程?
协程输出顺序差异:runBlocking vs coroutineScope
原代码(runBlocking版本)
fun main(){ println("start") runBlocking { for (i in 0..10){ launch { delay(2000L) println(i) } } } println("end") }
输出结果:
start 0 1 2 3 4 5 6 7 8 9 10 end
改写后的代码(coroutineScope + suspend main版本)
suspend fun main(){ println("start") coroutineScope { for (i in 0..10){ launch { delay(2000L) println(i) } } } println("end") }
输出特点:
数字0-10为随机顺序输出
现象原理分析
1. runBlocking的调度器特性
runBlocking默认使用BlockingEventLoop,它的事件循环运行在当前线程(单线程模式)。循环启动的11个协程会被依次加入这个单线程的事件队列。
每个协程执行delay(2000L)后会挂起,2秒后重新进入事件队列等待执行。由于事件队列是**先进先出(FIFO)**的规则,第一个启动的协程(i=0)会最先被调度恢复,接着是i=1,以此类推,最终输出自然有序。
简言之:runBlocking默认单线程调度,挂起协程恢复时按启动顺序排队执行,所以输出有序。
2. suspend main + coroutineScope的调度器特性
当使用suspend fun main()时,Kotlin默认分配Dispatchers.Default调度器——这是一个基于线程池的多线程调度器。
coroutineScope会继承外层的调度器,因此循环启动的协程会被分配到线程池的不同线程中执行。delay(2000L)结束后,各个协程的恢复时间受线程调度的不确定性影响:哪个协程所在的线程先空闲、被调度,哪个就先输出数字,最终呈现随机顺序。
简言之:suspend main默认用多线程调度器,协程在不同线程恢复,执行顺序由线程调度决定,所以输出随机。
补充验证
如果给runBlocking指定多线程调度器,输出也会变成随机顺序,直接印证调度器是核心原因:
fun main(){ println("start") runBlocking(Dispatchers.Default) { // 指定多线程调度器 for (i in 0..10){ launch { delay(2000L) println(i) } } } println("end") }
内容的提问来源于stack exchange,提问作者Max0r
相关产品推荐
相关产品推荐

