为何Kotlin协程未在挂起点(delay)处停止?
问题解析:协程取消未生效的原因
你遇到的问题核心在于**withContext切换到的上下文拥有独立的Job,不受原协程Job取消的影响**,且协程取消仅在挂起点才会触发终止。
代码行为拆解
Job的独立性:
scope1的Job是parentJob1,job1是它的子Job;scope2的Job是parentJob2,和parentJob1完全独立,没有父子关联。- 当你调用
job1.cancel()或parentJob1.cancel()时,只会取消job1及其关联的子Job,但withContext(scope2.coroutineContext)块内的协程绑定的是parentJob2,这个Job并未被取消,所以该块内的代码会完整执行——包括delay(1000)之后的打印语句。
协程取消的响应时机:
- 协程被取消后,并不会立即终止,只有当它遇到挂起点(比如
delay、yield等)时,才会抛出CancellationException并终止执行。 - 在你的代码中,
withContext块执行完毕后,回到job1的协程逻辑,此时job1已经被取消,但后续代码println("Coroutine in scope1 terminated")没有挂起点,所以会直接执行完成,不会触发取消终止。
- 协程被取消后,并不会立即终止,只有当它遇到挂起点(比如
对应你的观察点
- (i) 调用
parentJob1.cancel()行为一致:因为parentJob1是job1的父Job,取消父Job会递归取消所有子Job,但scope2的Job和它无关联,所以withContext块内的代码依然不受影响,执行流程和之前一致。 - (ii) 加挂起点后协程停止:如果在
withContext块后添加挂起点(比如delay(100)),那么job1的协程执行到这个挂起点时,会检测到自身已被取消,抛出CancellationException,后续的打印语句就不会执行了。
验证修改示例
如果要让withContext块内的代码也响应job1的取消,可以将scope2的Job设置为job1的子Job,比如:
val scope2 = CoroutineScope(parentJob2 + job1) // 将job1加入scope2的上下文
这样当job1被取消时,scope2的协程也会被取消,delay(1000)会触发终止,后续打印不会执行。
内容的提问来源于stack exchange,提问作者excitedGoose
相关产品推荐
相关产品推荐

