与SupervisorJob协同工作-取消行为

时间:2019-08-05 09:43:37

标签: android exception kotlin kotlin-coroutines

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

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

输出符合我的预期;

enter image description here

问题是,在暂停功能中,我将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)
            }

我得到了这个输出;

enter image description here

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

1 个答案:

答案 0 :(得分:1)

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

  

如果此作用域中有任何未处理的异常(例如,来自在此作用域中以Throwable开头的崩溃协程,则该方法可能会抛出对应的未处理的launch)。

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

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

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

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