3. 协程取消
任务说明
本案例目标:启动一个长时间运行的协程,在指定延迟后通过 cancel() 取消它,并正确处理取消时抛出的 CancellationException,验证协程确实被取消且资源得到释放。
核心实现
import kotlinx.coroutines.*
fun main() = runBlocking {
// 启动一个模拟耗时任务的协程
val job = launch {
try {
println("协程开始执行")
// 模拟耗时操作:循环打印并延迟
repeat(100) { index ->
println("处理中: $index")
delay(500) // 挂起点,可响应取消
}
println("协程正常完成") // 不应执行到此处
} catch (e: CancellationException) {
// 捕获取消异常,做清理工作
println("协程被取消,执行清理: ${e.message}")
// 清理后重新抛出,保持取消状态
throw e
}
}
// 主协程等待 1200ms 后取消子协程
delay(1200)
println("主协程发起取消")
job.cancel() // 请求取消
job.join() // 等待协程真正结束
println("协程已取消,isActive=${job.isActive}, isCancelled=${job.isCancelled}")
println("主协程结束")
}
关键机制说明
上述代码中,launch 返回的 Job 对象用于控制协程生命周期。调用 cancel() 后,协程会在下一个挂起点(即 delay())抛出 CancellationException。该异常被 catch 块捕获后,我们执行必要的清理逻辑(此处仅打印),然后必须重新抛出该异常——这是协程取消协作机制的要求,否则协程状态会不一致。最后通过 join() 等待协程完全结束,确保取消流程完成。
验证方法
直接运行上述代码,观察控制台输出。预期输出顺序为:
协程开始执行
处理中: 0
处理中: 1
处理中: 2
主协程发起取消
协程被取消,执行清理: StandaloneCoroutine was cancelled
协程已取消,isActive=false, isCancelled=true
主协程结束
判定标准:
- 输出中出现“协程被取消”且包含清理信息,说明
CancellationException被正确捕获处理; - 输出中不出现“协程正常完成”,说明协程确实在完成前被取消;
- 最后打印的
isActive=false和isCancelled=true表明协程已处于取消完成状态。
取消协作要点
协程取消是协作式的:只有挂起函数(如 delay、withContext 等)会响应取消。若协程内只有纯计算代码而没有挂起点,则 cancel() 不会立即生效,需配合 isActive 检查或 ensureActive() 手动处理。本案例使用 delay() 作为挂起点,是最典型的取消响应模式。
运行方式
本案例为纯 Kotlin/JVM 项目,需在 build.gradle.kts 中添加依赖:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
将代码保存为 Main.kt,执行 gradle run 或直接在 IDE 中运行 main() 函数即可观察输出。