0Pricing
Kotlin Academy · 课时

协作式取消:isActive 与 ensureActive

通过检查 isActive 并调用 ensureActive,使协程支持取消。

协作式取消:isActive 与 ensureActive 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

为什么需要协作式取消

Kotlin 协程使用协作式取消:只有在被取消的协程通过检查自身取消状态进行协作时,它才会停止。紧凑的 CPU 循环在执行检查之前不会停止。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        var i = 0
        while (true) { i++ } // never checks — never cancels!
    }
    delay(100)
    job.cancel()
    println("cancel sent but loop kept running until process ended")
}

检查 isActive

isActive 是 CoroutineScope 上的一个属性,在收到取消请求后会返回 false。在循环中检查它可以让协程进行协作。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        var i = 0
        while (isActive) {
            i++
        }
        println("Loop exited after $i iterations")
    }
    delay(10)
    job.cancel()
    job.join()
}

ensureActive()

如果协程已被取消,ensureActive() 会抛出 CancellationException。它可以直接替代 if (!isActive) throw CancellationException()。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            repeat(1_000_000) {
                ensureActive()  // throws if cancelled
                // heavy computation here
            }
        } catch (e: CancellationException) {
            println("Cancelled at iteration")
            throw e
        }
    }
    delay(5)
    job.cancel()
    job.join()
}

yield() 作为协作检查点

yield() 会短暂挂起协程,使调度器能够检查取消状态并运行其他协程。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        repeat(10) { i ->
            yield()  // cooperative cancellation point + gives up scheduler
            println("Step $i")
        }
    }
    delay(2)
    job.cancel()
    job.join()
    println("Done")
}

isActive 与 ensureActive

对于条件逻辑(例如优雅地跳出循环),请使用 isActive。当您希望立即将取消作为异常传播时,请使用 ensureActive()。

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        repeat(100) { i ->
            if (!isActive) {
                println("Stopping at $i, cleaning up...")
                return@launch
            }
            // work
        }
    }.also { delay(5); it.cancel(); it.join() }
}

CancellationException 很特殊

CancellationException 不会传播给父级——它表示正常取消。切勿吞掉它;请始终重新抛出它,或让它继续传播。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } catch (e: CancellationException) {
            println("Caught cancellation, rethrowing")
            throw e  // important!
        }
    }
    delay(50)
    job.cancel()
    job.join()
    println("Job state: ${job.isCancelled}")
}

CPU 密集型工作中的取消

对于 CPU 密集型工作,请在自然的检查点插入 ensureActive() 或 yield(),以便及时响应取消。

import kotlinx.coroutines.*
suspend fun heavyCompute(n: Int): Long {
    var result = 0L
    for (i in 0..n) {
        ensureActive()
        result += i
    }
    return result
}
fun main() = runBlocking {
    val job = launch(Dispatchers.Default) {
        println(heavyCompute(1_000_000))
    }
    delay(10)
    job.cancel()
    job.join()
}

自定义挂起函数中的 isActive

您可以在任何挂起函数中通过 coroutineContext.isActive,或使用 currentCoroutineContext().isActive 来访问 isActive。

import kotlinx.coroutines.*
suspend fun checkable() {
    while (currentCoroutineContext().isActive) {
        delay(10)
        println("tick")
    }
}
fun main() = runBlocking {
    val job = launch { checkable() }
    delay(35)
    job.cancel()
    job.join()
}

带资源清理的取消

即使采用协作式取消,也可以在 finally 块中清理资源——取消异常仍会穿过这些块继续传播。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            repeat(100) { i ->
                ensureActive()
                delay(20)
                println("Working $i")
            }
        } finally {
            println("Cleanup done")
        }
    }
    delay(50)
    job.cancel()
    job.join()
}

使用 isActive 轮询

轮询循环非常适合使用 isActive:持续轮询,直到协程被取消。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        while (isActive) {
            println("Polling...")
            delay(100)
        }
        println("Poller stopped")
    }
    delay(350)
    job.cancel()
    job.join()
}

与 Thread.interrupted() 的区别

与 Java 线程不同,Kotlin 协程不依赖中断标志。取消通过异常和作用域状态来建模,因此可以组合并保持结构化。

import kotlinx.coroutines.*
fun main() = runBlocking {
    // Kotlin way: cooperative via isActive / ensureActive
    launch {
        ensureActive()
        println("Kotlin coroutine: cancellation is explicit and structured")
    }
}

快速检查

协程被取消时,哪个函数会立即抛出异常?

回顾

协作式取消需要显式的检查点:isActive 用于有条件地退出,ensureActive() 用于立即抛出异常,yield() 用于挂起并让出线程。切勿吞掉 CancellationException。

常见问题解答

「协作式取消:isActive 与 ensureActive」课时是免费的吗?

是的 — 「协作式取消:isActive 与 ensureActive」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「协作式取消:isActive 与 ensureActive」这节课中我会学到什么?

通过检查 isActive 并调用 ensureActive,使协程支持取消。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「协作式取消:isActive 与 ensureActive」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 协作式取消:isActive 与 ensureActive
  2. withTimeout 与 withTimeoutOrNull
  3. 使用 finally 与 NonCancellable 清理资源
  4. 协程层次结构中的取消传播
← 返回 Kotlin Academy