协程中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()生效,可做两处修改:
- 用协程挂起函数
delay(500)替代Thread.sleep(500),delay会主动检测取消状态,一旦协程被取消就会抛出CancellationException终止协程。 - 在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
相关产品推荐
相关产品推荐

