You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

协程中Job.cancel与Scope.cancel的差异解析

协程Job与Scope取消行为差异的原因解析

一、Job.cancel()未终止任务的原因

你的第一个测试用例里,调用job.cancel()后任务没停止,核心有两个关键点:

  • 协程取消是协作式的,依赖挂起函数检测状态
    协程取消不会强制终止代码,只是将isActive标记为false,需要代码主动检测并退出。但你使用的Thread.sleep(500)是Java线程的阻塞API,并非协程挂起函数,它既不会触发协程的取消检测,也不会响应协程的取消状态。
  • 循环未设置退出逻辑
    即使isActive变为false,你的代码仅打印取消提示,却没有在else分支添加break或return终止while(true)循环,导致线程会持续重复执行循环体。

二、CoroutineScope.cancel()能终止任务的原因

第二个测试用例里,调用newScope.cancel()后任务立即停止,核心原因是:
CoroutineScope.cancel()会取消该作用域的根Job,同时会向所有子协程的执行线程发送中断信号。你的协程运行在Dispatchers.IO调度器上,它基于线程池实现,当线程收到中断信号时,正在执行的Thread.sleep(500)会抛出InterruptedException,协程框架会捕获该异常并自动终止协程,从而停止循环。

补充:让Job.cancel()也能终止任务的修改方案

如果想让第一个例子的job.cancel()生效,可做两处修改:

  1. 用协程挂起函数delay(500)替代Thread.sleep(500),delay会主动检测取消状态,一旦协程被取消就会抛出CancellationException终止协程。
  2. 在else分支添加break或return退出循环。

修改后的代码示例:

@Test
fun coroutineTest7Fixed() = runBlocking {
    val job = launch(Dispatchers.IO) {
        var count = 0
        while(true){
            if(isActive){
                println("Hello ${count++}")
            }
            else {
                println("Job Cancelled, exiting loop")
                break // 退出循环
            }
            delay(500) // 用协程挂起函数替代Thread.sleep
        }
    }
    Thread.sleep(6000)
    job.cancel()
    Unit
}

内容的提问来源于stack exchange,提问作者Dishonered

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 09:11:54