You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用runBlocking时Kotlin协程代码执行顺序疑问:输出为何与预期不符?

Kotlin协程launch执行顺序问题解析

原问题代码与现象

原代码如下:

private fun runBlockingWithLaunch() {
    print(1)
    runBlocking {
        print(2)
        launch {
            print(3)
            delay(2_000)
            print(4)
        }
        print(5)
    }
    print(6)
}

预期输出是123546,但实际运行得到125346。

核心原因:协程的非抢占式调度

Kotlin协程默认是非抢占式调度——除非当前协程主动调用挂起函数(比如delay)让出线程执行权,否则其他协程无法打断当前协程的同步代码执行。

具体执行逻辑:

  1. 先执行print(1),随后进入runBlocking(它会阻塞当前线程,直到内部所有协程执行完毕)。
  2. 执行print(2),调用launch创建子协程,但这个子协程不会立即启动——当前主协程(runBlocking内部的协程)还在运行同步代码,没有主动挂起,线程不会切换到子协程。
  3. 主协程继续执行print(5),直到这行代码跑完,线程才会调度之前创建的子协程,执行print(3)。
  4. 子协程调用delay(2000)挂起,线程回到主协程等待;2秒后子协程恢复,执行print(4)。
  5. 最后runBlocking结束,执行print(6)。
    所以最终输出是125346。

修改版本的差异解释

版本1:主协程delay(2),子协程delay(1)

private fun runBlockingWithLaunch() {
    print(1)
    runBlocking {
        print(2)
        launch {
            delay(1) // 新增挂起
            print(3)
            delay(2_000)
            print(4)
        }
        delay(2) // 新增挂起
        print(5)
    }
    print(6)
}

输出:125346

原因:

  • 主协程执行到delay(2)主动挂起,线程开始调度子协程。
  • 子协程执行delay(1)后挂起,此时主协程的挂起时间还剩1ms(总共2ms),线程回到主协程等待剩余时间。
  • 主协程挂起时间到,先恢复执行print(5),之后线程才会再次调度子协程执行print(3)。

版本2:主协程delay(3),子协程delay(1)

private fun runBlockingWithLaunch() {
    print(1)
    runBlocking {
        print(2)
        launch {
            delay(1) // 新增挂起
            print(3)
            delay(2_000)
            print(4)
        }
        delay(3) // 新增挂起
        print(5)
    }
    print(6)
}

输出:123546

原因:

  • 主协程执行delay(3)挂起,线程调度子协程。
  • 子协程执行delay(1)后恢复,先执行print(3),接着调用delay(2000)再次挂起。
  • 此时主协程的挂起时间还剩2ms(总共3ms-已过1ms),线程继续等待主协程挂起结束,之后才执行print(5)。

内容的提问来源于stack exchange,提问作者umutcan sahin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 06:25:03