【问题标题】:Propagate exception from async to CoroutineExceptionHandler将异常从异步传播到 CoroutineExceptionHandler
【发布时间】:2021-04-15 07:26:52
【问题描述】:

我有一个async 协程,它可能会引发异常。如何将异常传播到我的CoroutineExceptionHandler?在 try/catch 附近使用 await 我能够捕获异常,但无论上下文如何,我似乎都无法将处理传播到处理程序:

val handler = CoroutineExceptionHandler { _, e -> e.printStackTrace() }
val context = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + handler
val scope = CoroutineScope(context)

val async = scope.async {
    throw Exception("Test exception")
}
runBlocking {
    try {
        async.await()
    } catch (e: Exception) {
        log.error("Exception thrown", e)
    }
    delay(1_000)
}

我已经尝试从 catch 中重新抛出异常,或者使用以下一些构造来包装它:

catch (e: Exception) {
    throw e
    // withContext(context) { throw e }
    // scope.launch { throw e }
    // supervisorScope { throw e }
}

遗憾的是,它们都没有将其传播给处理程序。我可以以某种方式将CoroutineExceptionHandlerasync 协同程序一起使用吗?

【问题讨论】:

  • 你确定你真的需要async吗?您的示例肯定不会激发它,您忽略了await) 的结果。您的async 是否执行您等待其结果的计算?或者它只是执行一项任务?通常,如果您在后台执行计算,那是因为您正在并行执行多个计算,然后使用它们的结果,所有这些都在一个工作单元的范围内。在这种情况下,您只需在该工作单元周围使用简单的 try-catch。
  • 是的,我的用例是流中的并行异步计算,然后我等待结果以维护上游顺序。我只是想让这个例子变得简单,显然以更高的混淆机会为代价:)
  • 难道你不能将所有并行工作包装在coroutineScope 中,在单个外部try-catch 中处理其中的任何错误吗?我认为这就是 Kotlin 希望你这样做的方式,而 CoroutineExceptionHandler 只是作为最后的机制来让你知道,由于你的程序中的一个错误,一个未处理的异常爆发了。正是在整个 Kotlin 协程设计中严格遵守这一意图,您在使用它时遇到问题。

标签: kotlin asynchronous kotlin-coroutines


【解决方案1】:

有几种方法可以让它发挥作用。首先,正如async documentation 中所述,它会在失败时取消父作业。这就是为什么scope.launch { throw e } 什么都不做,到那时作用域被取消,协程甚至没有启动。

然后,正如supervised coroutines documentation 中所述,要在受监督的上下文中处理未捕获的异常,您应该将处理程序安装到子上下文中。所以例如以下代码会将异常传递给您的处理程序

runBlocking {
   supervisorScope {
      launch(handler){
         async.await()
      }
   }
}   

另一种方法是将异常处理程序安装到协程的根范围内,例如像这样

runBlocking {
   GlobalScope.launch(handler) {
      async.await()
   }.join()
}

这里我使用GlobalScope 进行演示,但它可以是任何活动(非取消)范围。

coroutine exception handling doc 中有很多示例,我建议您也检查并运行它们。

【讨论】:

    【解决方案2】:

    不,你不能。

    async 返回一个 Deferred,这就像一个未来。异常处理程序不适用,因为如果有异常,它就是 Deferred 的返回值。

    来自文档:

    除此之外,async builder 总是捕获所有异常并在生成的 Deferred 对象中表示它们,因此它的 CoroutineExceptionHandler 也不起作用。

    来源:https://kotlinlang.org/docs/exception-handling.html#coroutineexceptionhandler

    【讨论】:

      猜你喜欢
      • 2012-10-22
      • 2016-10-25
      • 1970-01-01
      • 1970-01-01
      • 2016-08-26
      • 2015-12-25
      • 1970-01-01
      • 1970-01-01
      • 2014-07-28
      相关资源
      最近更新 更多