【问题标题】:Why does my Scala future not propagate an Error?为什么我的 Scala 未来不会传播错误?
【发布时间】:2019-05-20 10:42:09
【问题描述】:

此代码按预期工作:

it("should propagate exceptions") {
  intercept[RuntimeException] {
    val future = Future { Thread.sleep(10); sys.error("whoops"); 22 }
    Await.result(future, Duration.Inf)
  }.getMessage should equal ("whoops")
}

但这不是:

it("should propagate errors") {
  intercept[StackOverflowError] {
    val future = Future { Thread.sleep(10); throw new StackOverflowError("dang"); 22 }
    Await.result(future, Duration.Inf)
  }.getMessage should equal ("dang")
}

第二个测试中的未来永远不会回来。为什么Error 子类(而不是Exception 子类)不会终止我的未来?我应该如何处理Errors?

编辑:这可能与Why does Scala Try not catching java.lang.StackOverflowError? 相关,但不完全相同。我没有在这里使用Try。核心问题是Future 永远不会返回;我无法从中捕获任何错误,因为它只是挂起。

【问题讨论】:

标签: scala exception error-handling future


【解决方案1】:

reporter 设施用于灾难,它只是挂钩到线程的 UncaughtExceptionHandler,但看起来它只使用默认线程工厂即可开箱即用:

scala 2.13.0-M5> import concurrent._,java.util.concurrent.Executors
import concurrent._
import java.util.concurrent.Executors

scala 2.13.0-M5> val ec = ExecutionContext.fromExecutor(null, e => println(s"Handle: $e"))
ec: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl$$anon$3@5e7c141d[Running, parallelism = 4, size = 0, active = 0, running = 0, steals = 0, tasks = 0, submissions = 0]

scala 2.13.0-M5> val f = Future[Int](throw new NullPointerException)(ec)
f: scala.concurrent.Future[Int] = Future(<not completed>)

scala 2.13.0-M5> f
res0: scala.concurrent.Future[Int] = Future(Failure(java.lang.NullPointerException))

scala 2.13.0-M5> val f = Future[Int](throw new StackOverflowError)(ec)
Handle: java.lang.StackOverflowError
f: scala.concurrent.Future[Int] = Future(<not completed>)

scala 2.13.0-M5> val ec = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor, e => println(s"Handle: $e"))
ec: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl@317a118b

scala 2.13.0-M5> val f = Future[Int](throw new StackOverflowError)(ec)
f: scala.concurrent.Future[Int] = Future(<not completed>)
Exception in thread "pool-1-thread-1" java.lang.StackOverflowError
    at $line14.$read$$iw$$iw$$iw$$iw$.$anonfun$f$1(<console>:1)
    at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:659)
    at scala.util.Success.$anonfun$map$1(Try.scala:261)
    at scala.util.Success.map(Try.scala:209)
    at scala.concurrent.impl.Promise$Transformation.doMap(Promise.scala:420)
    at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:402)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

您可以构建一个在运行时注册未来的绑定,以及一个知道线程何时崩溃的安全等待。例如,您可能想重试具有较低最大递归深度的算法。

【讨论】:

    【解决方案2】:

    正如 cmets 中所指出的,这是 Why does Scala Try not catching java.lang.StackOverflowError? 的副本

    根据 Scala 文档。

    注意:Try 上的组合子只会捕获非致命异常(请参阅 >scala.util.control.NonFatal)。另一方面,严重的系统错误将被 >抛出。

    No Throwable -> 错误被 Try 捕获

    还回答您有关通常如何进行错误处理的问题。 在 Scala 中,您可以对可能导致异常的代码使用 try / catch(非常类似于 Java):

    try { 
    // ... Your dangerous code in here
    } catch {
     case ioe: IOException => ... //
     case e: Exception => ...
    } 
    

    而且你应该总是先有更具体的例外。

    您提供的代码如下所示: https://scastie.scala-lang.org/2DJXJ6ESS9ySJZSwSodmZg

    我还尝试了您的代码,它肯定会产生 StackOverFlow 错误。 但它不能像上面提到的链接解释的那样正确捕捉它。

    【讨论】:

    • 不确定你所说的“它会产生 StackOverflowError”是什么意思——当我运行它时,未来永远不会回来。测试只是挂起。我想知道这是否是版本问题——我在 2.11.8 上(目前无法升级)。
    • @Sasgorilla 如果您单击该链接,您可以在那里运行代码。它将在控制台中显示错误。无法捕获错误。如果您检查重复的问题,答案是您需要 Try 才能抓住它。有意义吗?
    • 抱歉——我不太关注try 的事情。在你的小提琴中,我仍然看到 Await 在 30 秒后超时。换句话说,我正在捕捉TimeoutException,而不是StackOverflowError。您能否分享一个使用try 传播StackOverflowError 的示例?
    猜你喜欢
    • 2021-09-12
    • 2013-09-25
    • 2012-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多