协程子Job取消未触发父Job取消?Job与SupervisorJob差异解析
问题1:为何取消子Job后其他子Job未被取消?
根据Job文档说明:「使用非CancellationException调用cancel会同时取消父Job」,但Job.cancel仅接受CancellationException类型参数。测试代码如下:
fun main() { val parentJob = Job() val scope = CoroutineScope(parentJob) suspend fun printText(text: String) { println("Before Delay: $text") delay(1000) println("After Delay: $text") } val job1 = scope.launch { printText("Job#1") } job1.invokeOnCompletion { println("Job#1 completed. Cause = $it") } val job2 = scope.launch { printText("Job#2") } job2.invokeOnCompletion { println("Job#2 completed. Cause = $it") } println(parentJob.children.joinToString { it.toString() } ) job1.cancel(CancellationException()) Thread.sleep(10000) }
输出:
Before Delay: Job#1 Before Delay: Job#2 StandaloneCoroutine{Active}@462d5aee, StandaloneCoroutine{Active}@69b0fd6f Job#1 completed. Cause = java.util.concurrent.CancellationException After Delay: Job#2 Job#2 completed. Cause = null Process finished with exit code 0
解答
文档描述的场景是子Job因未捕获的非CancellationException异常而失败,这种情况下才会触发父Job的取消,进而取消所有其他子Job。而你这里是主动调用job1.cancel(CancellationException()),属于协程的正常协作取消:CancellationException是协程取消的标准信号,这种取消不会向父Job传播,父Job和其他子Job会继续正常执行,所以job2不受影响。
问题2:Job与SupervisorJob的核心区别是什么?为何ViewModelScope要使用SupervisorJob?
扩展测试用例模拟Android ViewModel场景:创建CoroutineScope(Job())发起两个请求,其中一个抛出异常但被内部捕获,另一个任务仍正常执行。测试代码如下:
fun main() { val parentJob = Job() val scope = CoroutineScope(parentJob) suspend fun printText(text: String) { println("Before Delay: $text") if (text == "Job#1") { throw IllegalArgumentException("Test") } delay(1000) println("After Delay: $text") } val job1 = scope.launch { try { printText("Job#1") } catch (e: Exception) { println(e) } } job1.invokeOnCompletion { println("Job#1 completed. Cause = $it") } val job2 = scope.launch { printText("Job#2") } job2.invokeOnCompletion { println("Job#2 completed. Cause = $it") } println(parentJob.children.joinToString { it.toString() }) Thread.sleep(10000) }
输出:
Before Delay: Job#1 java.lang.IllegalArgumentException: Test Job#1 completed. Cause = null Before Delay: Job#2 StandaloneCoroutine{Active}@462d5aee After Delay: Job#2 Job#2 completed. Cause = null Process finished with exit code 0
核心区别
- 普通Job:遵循失败传播规则——如果任何子Job因未捕获的非CancellationException异常失败,会立即取消父Job,父Job的取消会进一步传播给所有其他子Job,导致整个协程树被取消。
- SupervisorJob:遵循失败隔离规则——子Job的失败(未捕获的非CancellationException异常)不会传播给父Job,其他子Job可以继续正常执行;但父Job的取消会正常传播给所有子Job。
ViewModelScope使用SupervisorJob的原因
ViewModel通常需要同时执行多个独立的任务(比如多个并行的网络请求、UI状态更新),这些任务彼此独立,某个任务的失败不应该影响其他任务的执行,也不应该导致ViewModel的整个协程作用域被取消。使用SupervisorJob可以实现子Job的失败隔离,保证单个任务的异常不会扩散到整个作用域,符合ViewModel的业务场景需求。
内容的提问来源于stack exchange,提问作者toffor
相关产品推荐
相关产品推荐

