Kotlin协程中CoroutineScope.launch与内部launch调用的差异解析
关于Kotlin结构化并发中两种launch调用的差异解析
问题背景
在学习Kotlin结构化并发时,以下两段代码的执行结果差异引发疑惑:
代码示例1:内部使用scope.launch
fun main(): Unit = runBlocking { other(this) } suspend fun other(scope: CoroutineScope) { val job = scope.launch { scope.launch { delay(200) println("e") } println("a") } job.invokeOnCompletion { println("Complete") } }
输出结果:
a Complete e
代码示例2:内部使用launch
fun main(): Unit = runBlocking { other(this) } suspend fun other(scope: CoroutineScope) { val job = scope.launch { launch { delay(200) println("e") } println("a") } job.invokeOnCompletion { println("Complete") } }
输出结果:
a e Complete
第一种情况中父Job在子Job完成前就结束,不符合结构化并发预期。原本以为scope.launch等价于内部的launch(即this.launch),但实际结果不同,需要明确两种调用的差异及原因。
差异解析
1. 第一种情况非结构化并发的原因
当在scope.launch的协程体内部再次调用scope.launch时,新创建的协程直接挂载到传入的外部scope(即runBlocking的根scope),而非外层job的子协程。
外层job执行完println("a")后就会进入完成状态,触发invokeOnCompletion打印Complete;而内部的scope.launch协程属于根scope的子协程,会继续运行,延迟200ms后打印e。此时外层job已经完成,因此出现a→Complete→e的顺序,这违背了结构化并发中“父协程等待所有子协程完成再结束”的规则。
2. 两种launch调用的核心差异
scope.launch(外部传入的scope):新协程的父Job是外部scope的根Job,与外层job是平级关系,不属于结构化并发的父子协程体系。外层job不会等待这个平级协程完成,因此自身可以提前结束。launch(协程体内部的默认调用):协程体内部的launch等价于this.launch,这里的this是外层scope.launch创建的当前协程的CoroutineScope。新协程的父Job是外层的job,属于结构化并发的子协程。根据规则,父协程必须等待所有子协程完成后才会进入完成状态,因此外层job会等待内部协程打印e后,才触发invokeOnCompletion打印Complete,符合结构化并发预期。
内容的提问来源于stack exchange,提问作者varunkr
相关产品推荐
相关产品推荐

