【问题标题】:Does the scope handle exceptions/failures silently under the hood?范围是否在后台默默地处理异常/失败?
【发布时间】:2020-06-24 18:26:36
【问题描述】:

我有以下 sn-p 用于测试目的;

fun main() {

   val myScope = CoroutineScope(Dispatchers.Default) + Job()

   myScope.launch {

       val job = async {
           delay(1000)
           throw RuntimeException("shiiiet")
       }

       try {
           job.await()
       } catch (ret: RuntimeException){
           throw RuntimeException("yooo!")
       }
   }

    try {
        Thread.sleep(5000)
    } catch(e: Exception){

    }

    println("wohoooo!") 
}

我以为流量永远不会到达最后一个“哇哦!”行,但我错了。我看到它打印在屏幕上。我认为launch 会将异常传播到父作用域的原因是,由于父作用域不处理它,它会在到达 print 语句时使 JVM 崩溃。

这是因为父作用域在子作用域失败后被取消,收到 CancellationException 并被忽略?

【问题讨论】:

  • 一个失败的顶级 launch 不会使 JVM 崩溃,也不应该崩溃。崩溃的线程也不会导致 JVM 崩溃。
  • 确实如此,但我不知何故认为异常会通过范围一直传播到主线程。
  • 默认调度器不负责主线程。在纯 Java 上,主线程甚至不运行事件循环,因此根本无法调度协程。您必须使用 runBlocking 在其上显式创建事件循环。

标签: kotlin exception coroutine kotlin-coroutines


【解决方案1】:

您在示例中尝试了多种 throwcatch 方法。

async 按预期工作 - 当您 await 为它工作时,您可以捕获异常。但是如果你launch 是协程,默认的Thread.uncaughtExceptionHandler 只是将结果打印到控制台。

即使你这样做了

myScope.launch {
    ....
}.invokeOnCompletion { e -> println("Exception: $e") }

您仍然会在控制台上获得额外的结果。

解释了传播规则和处理异常的不同类型的调用here

关于如何在“主”协程中捕获异常的示例:

fun main() = runBlocking {
    try {
        GlobalScope.launch {
            delay(10)
            throw Exception("You don't catch me in main.")
        }
        launch {
            delay(10)
            throw Exception("You catch me in main.")
        }
        delay(100)
        println("Never reached.")
    } catch(e: Exception) {
        println("Caught in main: ${e.cause}")
    }
    println("The end")
}

【讨论】:

  • 你的代码总是打印Never reached!,以及You can't see me. 此外,Dispatchers.Unconfined 不使用“主上下文”,不管它应该是什么意思,它启动并恢复协程在这些调用中调用launchresume 的位置,没有任何线程限制。
  • Ups,You can't see me 我的意思是You don't catch me in main()....我需要再次查看调度程序。给我几个小时……
  • 你不可能从一个线程中同时启动的协程中捕获异常,该线程已经继续做其他事情了。由于上述原因,您会看到从launch(Unconfined) 抛出的异常只是由于null-dispatch 的意外,它在没有调度的情况下执行。一旦协程挂起,该行为就会消失,例如,如果您在抛出异常之前添加delay(10)。但是,即使没有暂停,我也无法从launch 捕获异常。
  • @Mark 经过更多阅读和测试,我删除了答案的错误部分。感谢您指出了这一点。我添加了一个关于如何捕捉“主协程”的示例......
  • 您所做的是捕获因子协程失败而导致的CancellationException。您没有捕获并处理原始异常,只是将其附加为取消的原因。抛出CancellationException 的代码行是delay(100),因为它是一个可取消的挂起调用。但是,这并没有阻止 runBlocking 范围失败,因此这实际上并不构成异常处理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-03
  • 2019-08-02
  • 2021-02-04
相关资源
最近更新 更多