Scala Future 阻塞多个 Await.result() 调用
Scala Future blocking with multiple Await.result() calls
我不明白为什么这个取自 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,但我必须单击红色 "stop" 按钮,因为它不会终止。
这里有两件事在起作用:
您没有关闭 Executors.newFixedThreadPool(1)
线程池。最后调用 shutdownNow()
,否则非守护线程会阻塞 Java 退出。
第二件事是您的线程池中需要多个线程,因为单个线程被多个 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
值。
如何处理单线程: 通常,您应该只在需要结果的最晚可能点等待您的期货。这就是为什么与 Java 相比,Scala 让 await 变得更加困难。所以正确的做法是避免在 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) }
}
你link的文章也谈到了blocking { ... }
。如果您使用标准执行上下文而不使用自定义线程池,这可能会有所帮助,如 here.
所述
我不明白为什么这个取自 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,但我必须单击红色 "stop" 按钮,因为它不会终止。
这里有两件事在起作用:
您没有关闭
Executors.newFixedThreadPool(1)
线程池。最后调用shutdownNow()
,否则非守护线程会阻塞 Java 退出。第二件事是您的线程池中需要多个线程,因为单个线程被多个
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
值。
如何处理单线程: 通常,您应该只在需要结果的最晚可能点等待您的期货。这就是为什么与 Java 相比,Scala 让 await 变得更加困难。所以正确的做法是避免在 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) }
}
你link的文章也谈到了blocking { ... }
。如果您使用标准执行上下文而不使用自定义线程池,这可能会有所帮助,如 here.