作为启动上下文提供时 CoroutineExceptionHandler 不执行

2023-11-24

当我运行这个时:

fun f() = runBlocking {
    val eh = CoroutineExceptionHandler { _, e -> trace("exception handler: $e") }
    val j1 = launch(eh) {
        trace("launched")
        delay(1000)
        throw RuntimeException("error!")
    }
    trace("joining")
    j1.join()
    trace("after join")
}
f()

这是输出:

[main @coroutine#1]: joining
[main @coroutine#2]: launched
java.lang.RuntimeException: error!
    at ExceptionHandling$f9$1$j1$1.invokeSuspend(ExceptionHandling.kts:164)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
    at kotlinx.coroutines.ResumeModeKt.resumeMode(ResumeMode.kt:67)

根据文档协程异常处理程序, the eh我提供的处理程序应该被执行。但事实并非如此。这是为什么?


我相信答案就在本节中官方协程文档:

如果协程遇到 CancellationException 以外的异常,它会取消带有该异常的父级。此行为无法重写,用于为不依赖于 CoroutineExceptionHandler 实现的结构化并发提供稳定的协程层次结构。当其所有子级终止时,原始异常由父级处理。

这也是在这些示例中 CoroutineExceptionHandler 始终安装到在 GlobalScope 中创建的协程的原因。将异常处理程序安装到在主 runBlocking 范围内启动的协程是没有意义的,因为尽管安装了处理程序,但当其子协程异常完成时,主协程总是会被取消.

(强调我的)

这里描述的内容不仅适用于runBlocking and GlobalScope,但任何非顶级协程构建器和自定义范围。

举例说明(使用 kotlinx.coroutines v1.0.0):

fun f() = runBlocking {
    val h1 = CoroutineExceptionHandler { _, e ->
        trace("handler 1 e: $e")
    }
    val h2 = CoroutineExceptionHandler { _, e ->
        trace("handler 2 e: $e")
    }
    val cs = CoroutineScope(newSingleThreadContext("t1"))
    trace("launching j1")
    val j1 = cs.launch(h1) {
        delay(1000)
        trace("launching j2")
        val j2 = launch(h2) {
            delay(500)
            trace("throwing exception")
            throw RuntimeException("error!")
        }
        j2.join()
    }
    trace("joining j1")
    j1.join()
    trace("exiting f")
}
f()

Output:

[main @coroutine#1]: launching j1
[main @coroutine#1]: joining j1
[t1 @coroutine#2]: launching j2
[t1 @coroutine#3]: throwing exception
[t1 @coroutine#2]: handler 1 e: java.lang.RuntimeException: error!
[main @coroutine#1]: exiting f

请注意处理程序h1被执行,但是h2不是。这类似于处理程序GlobalScope#launch执行,但不提供给任何处理程序launch inside runBlocking.

TLDR

提供给某个范围的非根协程的处理程序将被忽略。提供给根协程的处理程序,该根协程由launch将被执行。根协程创建者async or produce将忽略任何处理程序。

这种行为是有道理的。如果子协程的处理程序处理了异常,则该协程的所有祖先(直到根协程)将没有明确的方法来继续执行,因为它们都直接或间接地依赖于子协程的成功。

类似的逻辑适用于async。如果根协程以async可以使用已安装的处理程序捕获异常,代码如何通过以下方式等待协程的结果await知道处理异常后如何继续执行吗?与launch,在这里,除非协程成功,否则代码无法继续。

因此,它只对launch根协程具有处理异常的能力。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

作为启动上下文提供时 CoroutineExceptionHandler 不执行 的相关文章

随机推荐