作为启动上下文提供时,CoroutineExceptionHandler不执行 [英] CoroutineExceptionHandler not executed when provided as launch context

查看:1065
本文介绍了作为启动上下文提供时,CoroutineExceptionHandler不执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

运行此命令时:

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处理程序.但事实并非如此.为什么会这样?

According to the documentation for CoroutineExceptionHandler, the eh handler I provided should be executed. But it's not. Why is that?

推荐答案

我相信答案就在

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

If a coroutine encounters exception other than CancellationException, it cancels its parent with that exception. This behaviour cannot be overridden and is used to provide stable coroutines hierarchies for structured concurrency which do not depend on CoroutineExceptionHandler implementation. The original exception is handled by the parent when all its children terminate.

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

This also a reason why, in these examples, CoroutineExceptionHandler is always installed to a coroutine that is created in GlobalScope. It does not make sense to install an exception handler to a coroutine that is launched in the scope of the main runBlocking, since the main coroutine is going to be always cancelled when its child completes with exception despite the installed handler.

(重点是我的)

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

What's described here applies not just to runBlocking and GlobalScope, but any non-top-level coroutine builder and custom scope.

要进行说明(使用kotlinx.coroutines v1.0.0):

To illustrate (using 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()

输出:

[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,但未执行.这类似于GlobalScope#launch执行时的处理程序,但不是提供给runBlocking内部任何launch的处理程序.

Note that handler h1 is executed, but h2 isn't. This is analogous to the handler on GlobalScope#launch executing, but not the handler provided to any launch inside runBlocking.

TLDR

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

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

As correctly pointed out by Marko Topolnik in the comments below, the above generalization only applies to coroutines created by launch. Those created by async or produce will always ignore all handlers.

这篇关于作为启动上下文提供时,CoroutineExceptionHandler不执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆