协程取消机制详解:掌握协作式资源回收方法
时间:2026-08-20 | 作者:风起客 | 阅读:0在并发编程领域,像 Thread.stop() 这种强行停止线程的做法,不仅危险重重,而且早已过时。
Kotlin协程则秉持着一种截然不同的理念:协作式取消(Cooperative Cancellation)。
这意味着,取消并非是一种强制性的命令,而是一种“请求”。协程需要主动检查自身是否已被取消,然后以优雅的方式交出执行权。
一、取消的触发:Scope 与 Job 的联动
在协程中,取消是沿着 Job 层级结构 传播的。
- 取消作用域 (Scope): 调用
scope.cancel()会取消该作用域启动的所有子协程。 - 取消特定 Job: 调用
job.cancel()只会取消该 Job 及其子项,不影响父级及兄弟级。
关键操作符对比
| 方法 | 行为说明 |
|---|---|
job.cancel() | 发出取消请求,但不等待协程真正结束。 |
job.join() | 挂起当前协程,直到该任务运行结束。 |
job.cancelAndJoin() | 发出取消请求并挂起,直到该任务彻底清理完毕。推荐用于确保资源释放的场景。 |
二、取消的核心:协作原则
划重点:如果协程正在执行繁重的 CPU 计算任务且没有检查取消状态,它将无法被取消。
如何让你的协程“可被取消”?
要让协程具备协作性,主流方案有两种。
1. 周期性检查 isActive
这是最直接的办法。
在 while 循环或耗时逻辑中,持续检查协程的存活状态。
Kotlin
val job = scope.launch(Dispatchers.Default) {
var i = 0
while (i < 5 && isActive) { // 检查 isActive 标志位
// 模拟耗时计算
println("Hello $i")
i++
}
}
2. 使用 ensureActive() 或 yield()
ensureActive():如果 Job 已取消,它会立即抛出CancellationException。yield():不仅检查取消状态,还会让出 CPU 执行权,让其他协程有机会运行,是处理重度循环的推荐方式。
Kotlin
val job = scope.launch(Dispatchers.Default) {
for (i in 1..1000) {
ensureActive() // 如果已取消,直接抛出异常结束
// 或者使用 yield()
// yield()
executeComputation()
}
}
三、挂起函数:天生支持取消
绝大多数官方提供的挂起函数,如 withContext、delay 等,都是可取消的。
它们在执行前都会检查当前协程的状态,并在取消时抛出 CancellationException。
如果你在 try-catch 中捕获了 Exception,请务必注意:不要吞掉 CancellationException。
Kotlin
// 错误示范:吞掉取消异常
try {
work()
} catch (e: Exception) {
// 捕获了所有异常,包括 CancellationException
// 导致协程无法正常进入取消状态
log("Error: $e")
}
// 正确示范:重新抛出
try {
work()
} catch (e: CancellationException) {
throw e // 必须重新抛出,让协程机制正常运行
} catch (e: Exception) {
log("Real error: $e")
}
四、清理收尾:finally 与 NonCancellable
当协程被取消时,它会进入 Cancelling 状态,并执行 finally 块中的逻辑。
例如,关闭数据库连接这类收尾操作,通常就应放在这里。
陷阱:在 finally 中调用挂起函数
当协程处于 Cancelling 状态时,普通的挂起函数会直接失效,并继续抛出 CancellationException。
如果你必须在清理时执行挂起操作,如向服务器发送“取消成功”的通知,就需要使用特殊的上下文。
Kotlin
val job = scope.launch {
try {
doWork()
} finally {
// 这里直接调用挂起函数会失败
// cleanupWork()
// 使用 NonCancellable 上下文
withContext(NonCancellable) {
println("正在执行最后的清理工作...")
delay(1000) // 现在可以正常执行挂起操作了
println("清理完毕")
}
}
}
五、最佳实践总结
- 保持协作: 确保耗时的计算逻辑中包含
isActive检查或调用yield()。 - 不滥用
NonCancellable: 它非常强大但也危险,仅用于资源释放逻辑,不要在里面执行核心业务逻辑。 - 理解异常模型:
CancellationException不会导致应用崩溃,它是协程用来正常终止流程的信号。 - 优先使用封装: 尽量利用官方提供的
withTimeout等自带取消机制的函数。
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。
相关文章
更多-
- 迅捷路由器怎么调信号最强,设置时要注意什么?
- 时间:2026-08-27
-
- vivo浏览器怎么卸不掉?原因和解决方法在这里
- 时间:2026-08-27
-
- OPPO R11s黑屏了,怎么强制恢复出厂设置?
- 时间:2026-08-27
-
- 飞利浦显示器包装盒有生产日期和保修期吗?怎么看?
- 时间:2026-08-27
-
- 联想新平板开机必须联网吗?怎么做?
- 时间:2026-08-27
-
- 平板横竖屏切换设置与问题解决
- 时间:2026-08-27
-
- 移动电源容量怎么测?要准备哪些工具?
- 时间:2026-08-27
-
- 荣耀90 Pro防水吗?防水级别多少?怎么用才安全
- 时间:2026-08-27
精选合集
更多大家都在玩
大家都在看
更多-
- 糖尿病完全不能吃糖吗
- 时间:2026-09-15
-
- 蚂蚁庄园小课堂2026年9月16日最新题目答案
- 时间:2026-09-15
-
- 小鸡答题今天的答案是什么2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园每日答题答案2026年9月16日
- 时间:2026-09-15
-
- 以下哪种粮食是酿造绍兴黄酒的主要原料 蚂蚁庄园今日答案9月16日
- 时间:2026-09-15
-
- 劝学名句“及时当勉励,岁月不待人”出自哪位诗人 蚂蚁庄园今日答案9.16
- 时间:2026-09-15
-
- 蚂蚁庄园今天答题答案2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园答题今日答案2026年9月16日
- 时间:2026-09-15
