我需要在代码中实现一些异常处理,因此我有以下从片段启动的协程测试代码;
private val scoped = CoroutineScope(Dispatchers.Default + SupervisorJob())
...
val handler = CoroutineExceptionHandler { _, exception ->
println("TAG-Caught $exception")
}
scope.launch(handler) {
val job1 = launch {
println("TAG-first job is running")
delay(200)
}
testParentChildWithExceptionWithSupervision()
launch {
println("TAG-third job is running")
}
}
方法testParentChildWithExceptionWithSupervision
的外观;
suspend fun testParentChildWithExceptionWithSupervision() {
supervisorScope {
val job1 = launch {
println("TAG-running first (inner) child")
delay(200)
throw ArithmeticException()
}
val job2 = launch {
job1.join()
println("TAG-First child is cancelled: ${job1.isCancelled}, but second one is still active")
delay(200)
}
job1.join()
println("TAG-second child is cancelled: ${job2.isCancelled}")
println("TAG-ENDING")
}
}
输出符合我的预期;
问题是,在暂停功能中,我将supervisorScope
更改为coroutineScope
时,我发现根范围(与SpervisorJob一起使用)不会与她的孩子一起生活;
suspend fun testParentChildWithExceptionWithoutSupervision() {
coroutineScope {
val job1 = launch {
println("HH-doing first child")
delay(200)
throw ArithmeticException()
}
val job2 = launch {
job1.join()
println("HH-First child is cancelled: ${job1.isCancelled}, but second one is still active")
delay(200)
}
我得到了这个输出;
因此,即使作用域具有主管工作,在出现异常后,似乎在根作用域中也不会进行任何操作。我打赌我想念某事,但看不到它。有人可以解释其背后的原因吗?
答案 0 :(得分:1)
如果您在suspend fun coroutineScope
上签出documentation,则会发现:
如果此作用域中有任何未处理的异常(例如,来自在此作用域中以
Throwable
开头的崩溃协程,则该方法可能会抛出对应的未处理的launch
)。
在您的代码中会发生这种情况:“第一个(内部)孩子”由于未处理的ArithmeticException
而崩溃。这成为testParentChildWithExceptionWithSupervision
的结果,在呼叫站点,没有人处理。因此,它也将导致父进程崩溃-不是通过传播协程取消的机制,而是通过基本异常机制。 SupervisorJob
在这里没什么区别,代码主块突然完成,但未处理该异常,这就是为什么您看到它由未处理的异常处理程序打印出来的原因。
如果您修改代码以执行此操作:
try {
testParentChildWithExceptionWithSupervision()
} catch (e: ArithmeticException) {
println("ArithmeticException in main block")
}
您将看到主要协程进行到最后。