我需要在代码中实现一些异常处理,因此我有以下从片段启动的协程测试代码;

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")
    }
}

输出与我预期的一样;

android - 协同主管工作-取消行为-LMLPHP

事情是,一旦我在挂起函数中将supervisorScope更改为coroutineScope,我就会发现根范围(与SpervisorJob一起使用)不会与她的 child 一起继续。
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)
            }

而我得到这个输出;

android - 协同主管工作-取消行为-LMLPHP

因此,即使作用域具有主管工作,在出现异常后,似乎也不会在根作用域中进行任何操作。我打赌我想念某事,但看不到它。有人可以解释其背后的原因吗?

最佳答案

如果您在suspend fun coroutineScope上 checkout documentation,则会发现:



这是在您的代码中发生的情况:“第一个(内部) child ”由于未处理的ArithmeticException崩溃。这成为testParentChildWithExceptionWithSupervision的结果,在调用站点,没有人处理它。因此,它也将导致父进程崩溃-不是通过传播协程取消的机制,而是通过基本异常机制。 SupervisorJob在这里没有什么区别,代码主块突然完成而未处理该异常,这就是为什么您看到它由未处理的异常处理程序打印的原因。

如果您修改代码以执行此操作:

    try {
        testParentChildWithExceptionWithSupervision()
    } catch (e: ArithmeticException) {
        println("ArithmeticException in main block")
    }

您会看到主要协程进行到最后。

10-08 03:45