Kotlin协程无挂起点时取消生效的原因解析
为何无挂起点的Kotlin协程仍会被取消?
launch { // If we comment 'cancel()' console shows: // B // C // A // So there is no suspension point. cancel() launch { println("A") } // Will not be printed coroutineScope { println("B") } println("C") // Will not be printed }.join()
当调用cancel()后,原本以为因为没有挂起点,取消不会生效,但实际协程执行完coroutineScope中的println("B")后就被取消了,后续的println("C")没有执行;而注释掉cancel()时,打印顺序是B、C、A,说明协程从未挂起。为什么取消操作在这里会生效?
核心原因:cancel()标记状态,coroutineScope主动检查取消
cancel()的作用是标记取消状态,而非立即终止
调用cancel()后,当前协程的取消状态会被置为true,但协程不会立刻停止运行——除非遇到挂起点、主动检查取消状态,或者调用了会检查取消状态的挂起函数。launch { println("A") }不执行的原因
在已被标记为取消的协程上下文中启动新协程时,新协程会直接继承取消状态,不会执行其块内代码,所以A不会被打印。coroutineScope是关键:挂起函数内置取消检查
尽管coroutineScope的块内没有挂起点,但它本身是一个挂起函数,内部包含了取消检查逻辑:- 首先,它会同步执行块内的
println("B")(因为块内没有子协程需要等待); - 在
coroutineScope准备返回前,它会主动检查当前协程的取消状态。发现已被标记取消后,会抛出CancellationException,这个异常会终止当前协程的执行,因此后续的println("C")不会运行。
- 首先,它会同步执行块内的
纠正误解:取消并非仅在挂起时生效
取消的核心是协程的取消状态被标记,任何主动检查该状态的代码都会触发终止逻辑。除了挂起函数会自动检查外,你也可以手动调用ensureActive()或yield()来检查取消状态,让协程在无挂起点的情况下也能响应取消。
内容的提问来源于stack exchange,提问作者mmog
相关产品推荐
相关产品推荐

