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() 作为挂起点,是最典型的取消响应模式。

协程取消时序图 主协程 子协程 t=0ms launch { 开始执行 } 处理中: 0,1,2... t=1200ms job.cancel() CancellationException 抛出 catch 块执行清理 throw e (保持取消) job.join() 返回 启动 取消 异常 清理 join返回

运行方式

本案例为纯 Kotlin/JVM 项目,需在 build.gradle.kts 中添加依赖:

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")

将代码保存为 Main.kt,执行 gradle run 或直接在 IDE 中运行 main() 函数即可观察输出。