coroutineScope 是一个挂起函数
suspend fun main() {
// Root of the coroutine subtree
coroutineScope { // this: CoroutineScope
this.launch {
this.launch {
delay(2.seconds)
println("Child of the enclosing coroutine completed")
}
println("Child coroutine 1 completed")
}
this.launch {
delay(1.seconds)
println("Child coroutine 2 completed")
}
}
// Runs only after all children in the coroutineScope have completed
println("Coroutine scope completed")
}
为什么不是先执行 println("Coroutine scope completed")
这段代码的执行顺序是由 Kotlin 协程的结构化并发机制决定的。核心原因是 coroutineScope 是一个挂起函数,它会等待其内部所有子协程完成之后才会继续执行后面的代码。
让我们逐步分析执行流程:
- 进入
coroutineScope块- 程序执行到
coroutineScope { ... }时,这是一个挂起点,会创建一个新的协程作用域。
- 程序执行到
- 启动第一个子协程
this.launch { ... }启动了一个子协程(称为 A)。- 在 A 内部:
- 首先遇到
println("Child coroutine 1 completed"),所以立即打印 "Child coroutine 1 completed"。 - 然后启动了一个嵌套的子协程 B:
this.launch { delay(2.seconds); println(...) }。 - 注意:B 是在 A 内部启动的,但
launch是非阻塞的,A 不会等待 B 完成就继续往下执行(实际上 A 已经没有更多代码了)。
- 首先遇到
- 启动第二个子协程
this.launch { delay(1.second); println("Child coroutine 2 completed") }启动了另一个子协程 C。爱吃喝
coroutineScope等待所有子协程完成coroutineScope必须等待 A、B、C 全部完成后,才能继续执行后面的println("Coroutine scope completed")。- 此时主线程没有被阻塞(因为协程是轻量级的),但控制流被挂起在这里。
- 时间线展开
- t=0s: 打印 "Child coroutine 1 completed"(A 中的第一行代码立刻执行)。
- t=1s: 协程 C 的
delay(1.second)结束,打印 "Child coroutine 2 completed",然后 C 完成。 - t=2s: 协程 B 的
delay(2.seconds)结束,打印 "Child of the enclosing coroutine completed",然后 B 完成。 - 此时 A 其实早就完成了(它只有两行代码,第二行是启动 B 而已),B 也完成了,C 也完成了。
coroutineScope的所有子协程都结束了。
- 退出
coroutineScope- 挂起点恢复,执行
println("Coroutine scope completed")。
- 挂起点恢复,执行
Child coroutine 1 completed
Child coroutine 2 completed
Child of the enclosing coroutine completed
Coroutine scope completed关键点总结:
coroutineScope是一个结构化并发的边界,它保证在其内部启动的所有协程完成后才返回。https://www.ihechi.comlaunch启动的协程是并发的,不阻塞父协程的执行,但父作用域会等待它们全部结束。- 因此,最后的
println("Coroutine scope completed")一定是最后执行的,因为它位于coroutineScope块之外,且coroutineScope本身是挂起函数。





