首页 文章

Scala Future使用多个Await.result()调用阻塞

提问于
浏览
0

我不明白为什么这个来自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,但我必须单击红色的“停止”按钮,因为它不会终止 .

1 回答

  • 2

    这里有两件事:

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

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

    Why you need more than one thread: 如果在等待乘法之前添加调试消息

    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 值 .

    How to do with single thread: 一般来说,您只能在需要结果的最新点等待您的期货 . 这就是为什么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所述 .

相关问题