Kotlin协程coroutineScope内launch及后续代码为何不在Main线程运行
问题1:为什么launch启动的协程没有运行在Main线程
你的核心认知偏差是:默认认为协程启动后会自动绑定Main线程,实际上协程运行的线程完全由其上下文携带的CoroutineDispatcher(调度器)决定。
你遇到的现象是JVM平台(非Android)的默认特性导致的:
- 非Android的JVM环境下,
Dispatchers.Main默认没有实现,需要额外引入对应UI框架(Swing/JavaFX等)的协程绑定依赖才能使用,否则直接指定Dispatchers.Main甚至会抛出异常。 coroutineScope本身不会修改协程上下文,只会完全继承父协程的调度器,所以它本身不会导致线程切换。你代码中的launch没有显式指定调度器,默认会继承父协程的调度器。你用的suspend fun main在部分版本的协程库实现中,默认父调度器是多线程的Dispatchers.Default,所以启动的子协程会被分配到Default调度器的工作线程池中运行。- 你看到初始日志运行在main线程,只是因为
suspend fun main的启动入口是由main线程触发的,和协程本身的调度器没有关系。
如果想要子协程都运行在main线程,可以把入口改成显式的runBlocking包裹:
fun main() = runBlocking { // 此处替换你原来main函数内的所有代码 // runBlocking的默认调度器是当前调用的main线程,内部launch默认继承该调度器 }
问题2:为什么coroutineScope之后的日志由工作线程输出
这个现象的原因和第一个问题同源:
coroutineScope是挂起函数,它会等待内部所有子协程全部执行完毕后,才会恢复父协程的执行。- 只有当协程使用单线程绑定的调度器时,挂起恢复后才会固定回到原来的线程。你的父协程默认用的是多线程的
Dispatchers.Default,恢复时会调度到线程池中的任意可用工作线程,所以后续的日志就输出到了工作线程。
如果用上述的显式runBlocking作为入口,runBlocking的调度器是和main线程绑定的单线程调度器,挂起恢复后就一定会回到main线程,就不会出现这个问题。
内容的提问来源于stack exchange,提问作者Garrick
相关产品推荐
相关产品推荐

