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

时间:2018-12-02 00:08:28

标签: kotlin coroutine kotlinx.coroutines

运行此命令时:

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)

根据CoroutineExceptionHandler的文档,应执行我提供的eh处理程序。但事实并非如此。为什么会这样?

2 个答案:

答案 0 :(得分:1)

您的kotlinx.coroutines版本是什么?由于不推荐使用0.26.0独立launch构建器,因此您应该使用GlobalScope.launch

我尝试了您的样本,并且在更改之后它起作用了。

Kotlinx.coroutines changelog

答案 1 :(得分:1)

我相信答案就在official coroutines docs的这一部分:

  

如果协程遇到除CancellationException以外的异常,它将取消具有该异常的父对象。此行为不能被覆盖,并且用于为不依赖于CoroutineExceptionHandler实现的结构化并发提供稳定的协程层次结构。当父级的所有子级终止时,父级将处理原始异常。

     

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

(重点是我的)

这里描述的内容不仅适用于runBlockingGlobalScope,而且还适用于任何非顶级协程生成器和自定义范围。

说明:

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()

输出:

[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内部提供给任何runBlocking的处理程序。

TLDR

提供给范围的非根协程的处理程序将被忽略。提供给根协程的处理程序将被执行。

正如Marko Topolnik在下面的评论中正确指出的那样,以上概括仅适用于launch创建的协程。由asyncproduce创建的代码将始终忽略所有处理程序。