简体   繁体   English

为什么我的Scala未来无法传播错误?

[英]Why does my Scala future not propagate an Error?

This code works as expected: 此代码按预期工作:

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")
}

But this doesn't: 但这不是:

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")
}

The future in this second test never returns. 在第二次测试中的未来永远不会回来。 Why doesn't an Error subclass (as opposed to an Exception subclass) terminate my future? 为什么Error子类(与Exception子类相对)不能终止我的未来? How should I handle Error s? 我应该如何处理Error

EDIT: This is possibly related, but not identical, to Why does Scala Try not catching java.lang.StackOverflowError? 编辑:这可能与为何Scala尝试不捕获java.lang.StackOverflowError相关但不完全相同 . I'm not using Try here. 我不在这里使用“ Try The core issue is that the Future never returns at all; 核心问题是, Future永远不会回来。 I can't catch any error from it because it just hangs. 我无法捕获任何错误,因为它只是挂起。

As pointed out in the comments, this is a duplicate of Why does Scala Try not catching java.lang.StackOverflowError? 正如评论中指出的那样,这与“ 为什么Scala尝试不捕获java.lang.StackOverflowError?”的副本相同

According to Scala documentation. 根据Scala文档。

Note: only non-fatal exceptions are caught by the combinators on Try (see >scala.util.control.NonFatal). 注意:Try上的组合器仅捕获非致命异常(请参见> scala.util.control.NonFatal)。 Serious system errors, on the other hand, will be >thrown. 另一方面,将引发严重的系统错误。

No Throwable -> Errors are catched by Try No Throwable-> Try捕获错误

Also to answer your question about how error handling is usually done. 还可以回答有关通常如何进行错误处理的问题。 In Scala you can use try / catch for code that can cause exceptions (very similar to Java): 在Scala中,您可以对可能导致异常的代码使用try / catch(非常类似于Java):

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

And you should always have the more specific exceptions first. 而且,您应该始终首先拥有更具体的例外。

The code you provided would look something like this: https://scastie.scala-lang.org/2DJXJ6ESS9ySJZSwSodmZg 您提供的代码如下所示: https : //scastie.scala-lang.org/2DJXJ6ESS9ySJZSwSodmZg

Also I tried out your code and it definitely produces the StackOverFlowerror. 我也尝试了您的代码,它肯定会产生StackOverFlowerror。 But it can't catch it properly like the above mentioned link explains. 但是它不能像上面提到的链接所说明的那样正确捕获它。

The reporter facility is for catastrophes, which just hooks into the thread's UncaughtExceptionHandler , but it looks like it works out of the box with just the default thread factory: 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>)

whereas

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)

You could construct a rig that registers a future when it runs, and a safe await that knows when threads have blown up. 您可以构建一个可在运行时记录未来的装备,并安全地等待何时线程被炸毁。 Maybe you want to retry an algorithm with a lower max recursion depth, for example. 例如,也许您想重试具有较低最大递归深度的算法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM