【问题标题】:Scala Future blocking with multiple Await.result() callsScala Future 阻塞多个 Await.result() 调用
【发布时间】:2016-05-11 14:11:28
【问题描述】:

我不明白为什么这个取自 here 的示例会阻塞

import java.util.concurrent.Executors
import scala.concurrent._
import scala.concurrent.duration.Duration

object Main {
  def main(args: Array[String]) {
    println("Start")
    implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(1))
    println("Made ec")
    def addOne(x: Int) = {
      println("Executing addOne")
      Future(x)
    }

    def multiply(x: Int, y: Int): Future[Int] = Future {
      val a = addOne(x)
      val b = addOne(y)
      val result: Future[Int] = for (r1 <- a; r2 <- b) yield r1 * r2
      println("Got result")
      val awaitedResult: Int = Await.result(result, Duration.Inf)
      println("awaitedResult = " + awaitedResult)
      awaitedResult
    }
    val mult: Future[Int] = multiply(2,2)
    val multResult: Int = Await.result(mult, Duration.Inf)
    println("Result of mult = " + multResult)
  }
}

IntelliJ Scala 项目的输出:

Connected to the target VM, address: '127.0.0.1:36346', transport: 'socket'
Start
Made ec
Executing addOne
Executing addOne
Got result
awaitedResult = 4
Result of mult = 4
Disconnected from the target VM, address: '127.0.0.1:36346', transport: 'socket'

Process finished with exit code 130

注意:

    Hot Swap failed Main: hierarchy change not implemented;
    Main: Operation not supported by VM

奇怪的是,在 IntelliJ 控制台中,我看到了答案,4,但我必须单击红色的“停止”按钮,因为它不会终止。

【问题讨论】:

  • 为什么会阻塞?因为每次调用Await.result 时都会阻塞,这正是这个方法的作用,它阻塞直到Future 结束。

标签: scala intellij-idea future blocking


【解决方案1】:

这里有两件事在起作用:

  1. 您没有关闭您的Executors.newFixedThreadPool(1) 线程池。最后调用shutdownNow(),否则非守护线程阻塞Java退出。

  2. 第二件事是您的线程池中需要多个线程,因为单个线程被多个Await 调用阻塞。

为什么需要多个线程:如果在等待乘法之前添加调试消息

val mult: Future[Int] = multiply(2,2)
println("before multiplication")
val multResult: Int = Await.result(mult, Duration.Inf)

你会看到输出可能是

Made ec
before multiplication result
Executing addOne
...

如您所见,调用multiply(2,2) 只是构造一个包含您的计算处方的Future,但不一定执行任何操作。

这里是你遇到死锁的方法:首先,Await.result(mult, Duration.Inf) 触发multiply() 方法并阻塞主线程。 multiply() 的主体在单线程池线程上执行。然后我们点击Await.result(result, Duration.Inf)。这再次触发result 值的计算并阻塞线程池线程。这就是我们遇到死锁的地方:线程池因等待Await.result(result, Duration.Inf) 而被阻塞,但没有其他线程可以计算result 的值。

如何处理单线程:通常,您应该只在需要结果的最晚可能点等待您的未来。这就是为什么 Scala 比 Java 更难等待。所以正确的做法是避免在multiply()中等待:

def multiply(x: Int, y: Int): Future[Int] = {
  val a = addOne(x)
  val b = addOne(y)
  val result: Future[Int] = for (r1 <- a; r2 <- b) yield r1 * r2
  println("Got result")
  result.andThen{ case Success(i) => println("awaitedResult = " +i) }
}

您链接的文章还谈到了blocking { ... }。如果您使用标准执行上下文而不是自定义线程池,这可能会有所帮助,如here 所述。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-15
    • 2011-08-04
    • 2017-08-20
    • 2017-09-17
    • 2013-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多