具有 SupervisorJob 的协程 - 取消行为

std*_*out 5 android exception kotlin kotlin-coroutines

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

private val scoped = CoroutineScope(Dispatchers.Default + SupervisorJob())

...

val handler = CoroutineExceptionHandler { _, exception ->
        println("TAG-Caught $exception")
    }

scoped.launch(handler) {

        val job1 = launch {
            println("TAG-first job is running")
            delay(200)
        }

        testParentChildWithExceptionWithSupervision()

        launch {
            println("TAG-third job is running")
        }
    }
Run Code Online (Sandbox Code Playgroud)

该方法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")
    }
}
Run Code Online (Sandbox Code Playgroud)

输出如我所料;

在此输入图像描述

问题是,一旦我在挂起函数中更改supervisorScopecoroutineScope,我就会看到根范围(使用 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)
            }
Run Code Online (Sandbox Code Playgroud)

我得到这个输出;

在此输入图像描述

因此,在发生异常后,根范围内似乎没有进行任何操作,即使该范围有主管工作。我敢打赌我想念某件事但看不到它。有人可以解释一下其背后的原因吗?

Mar*_*nik 1

如果您查看上的文档suspend fun coroutineScope,您会发现:

Throwable如果此范围内存在任何未处理的异常(例如,来自在此范围内启动的崩溃协程) ,则该方法可能会抛出 [...] 相应的未处理异常launch

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

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

    try {
        testParentChildWithExceptionWithSupervision()
    } catch (e: ArithmeticException) {
        println("ArithmeticException in main block")
    }
Run Code Online (Sandbox Code Playgroud)

你会看到主协程一直进行到最后。