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

Kotlin协程线程池调度器下delay()后任务未完成是否因协程被阻塞?

问题核心原因

你的问题本质是自定义调度器被提前关闭,和delay的实现逻辑无关,delay是标准的无阻塞挂起函数,既不会阻塞线程,也不会阻塞协程。


两种场景的差异分析

1. 前置知识:use函数的特性

Kotlin的use函数等价于Java的try-with-resources语法,只要use的代码块执行完毕,会自动关闭内部的资源。你代码中use包裹的资源是线程池转换来的协程调度器,关闭调度器时会同步 shutdown 背后的线程池,不再接受任何新的协程调度任务。

另外launch是异步非阻塞调用,调用后会立即返回,不会等待协程执行完成。


2. 用delay时日志缺失的执行流程

1. 进入use块,启动coroutine#2(绑定自定义线程池调度器),打印start日志后调用delay(100),协程挂起释放线程
2. 主线程继续执行,启动coroutine#3(绑定全局IO调度器),打印start日志后调用delay(1000),协程挂起释放线程
3. 主线程打印called tasks,此时use块的所有代码执行完毕,自动触发调度器关闭,自定义线程池被shutdown
4. 100ms到后coroutine#2需要恢复执行,但绑定的调度器已经关闭,无法分配线程执行后续逻辑,end日志无法打印
5. 1000ms到后coroutine#3绑定的全局IO调度器未被关闭,正常恢复执行打印end日志
6. runBlocking结束,打印done

3. 用yield时日志正常的执行流程

1. 进入use块,启动coroutine#2,打印start日志后调用yield()
2. yield()的作用是主动让出当前线程,不会等待固定时长,仅会把协程放回待调度队列,因为此时调度器还未关闭,coroutine#2马上被重新调度执行,打印end日志,协程执行完毕
3. 主线程继续执行启动coroutine#3,打印called tasks,此时use块执行完毕关闭调度器,但是coroutine#2已经跑完,完全不受影响
4. coroutine#3绑定全局IO调度器,正常执行完成打印end日志

解决方案

如果要等待自定义调度器上的协程全部执行完成再关闭资源,可以在use块内加coroutineScope包裹逻辑,coroutineScope会等待所有子协程执行完成后再退出:

Executors.newFixedThreadPool(2)
    .asCoroutineDispatcher().use { dispatcher ->
        coroutineScope {
            launch(dispatcher + handler) { task(100) }
            launch(Dispatchers.IO + handler) { task(1000) }
            println("called tasks ${Thread.currentThread()}")
        }
    }

也可以手动调用join等待两个协程任务完成:

val job1 = launch(dispatcher + handler) { task(100) }
val job2 = launch(Dispatchers.IO + handler) { task(1000) }
println("called tasks ${Thread.currentThread()}")
job1.join()
job2.join()

内容的提问来源于stack exchange,提问作者Mike Zhao

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 17:21:00