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
相关产品推荐
相关产品推荐

