我最近在一台机器上遇到了一些奇怪的行为,map
一个返回Future[T]
的函数正在顺序执行。其他机器上也不会出现同样的问题:工作是按照预期交错进行的。后来我发现这很可能是因为Scala有点过于聪明并且选择了与机器资源相匹配的ExecutionContext
:一个核心,一个工人。
这里有一些简单的代码可以重现问题:
import scala.concurrent._
import scala.concurrent.duration._
val ss = List("the", "quick", "brown", "fox",
"jumped", "over", "the", "lazy", "dog")
def r(s: String) : Future[String] = future {
for (i <- 1 to 10) yield {
println(String.format("r(%s) waiting %s more seconds...",
s, (10 - i).toString))
Thread.sleep(1000)
}
s.reverse
}
val f_revs = ss.map { r(_) }
println("Look ma, no blocking!")
val rev = f_revs.par.map { Await.result(_, Duration.Inf) }.mkString(" ")
println(rev)
在表现出奇怪行为的机器上运行它会产生如下的顺序输出:
Look ma, no blocking!
r(the) waiting 9 more seconds...
r(the) waiting 8 more seconds...
r(the) waiting 7 more seconds...
提供自定义ExecutionContext
:
val pool = Executors.newFixedThreadPool(1)
implicit val ec = ExecutionContext.fromExecutor(pool)
允许线程在此计算机上交错。但是我现在遇到了一个新问题:线程池没有关闭,导致程序无限期挂起。显然,这是FixedThreadPool
的{{3}},我可以通过将pool.shutdown()
放在某处来关闭它。
叫我顽固,但我不想告诉线程池关闭。当所有队列都为空(可能在一段延迟之后)时,有没有办法将池配置为关闭,就像它与默认池一样?我查看过ExecutionContext
文档,但我找不到我要找的内容。
答案 0 :(得分:1)
看起来Java 7有一些额外的ExecutorService
,特别是ForkJoinPool,它可以满足我的需求(即,不需要shutdown()
池。)
将池更改为以下内容足以实现我想要的目标:
val pool = new java.util.concurrent.ForkJoinPool(5)
Java 8显然有even more services。
答案 1 :(得分:1)
Scala使用自己的fork-join实现,其行为与Java不同,因此默认ExecutionContext
与您使用Executors
创建的行为之间的行为不同。
执行所需操作的更简单方法是设置以下系统属性以配置默认ExecutionContext
:
scala.concurrent.context.minThreads
强制执行最少数量的线程。默认为1
。scala.concurrent.context.numThreads
设置线程数。默认为x1
。scala.concurrent.context.maxThreads
强加最大线程数。默认为x1
。这些中的每一个都可以是数字或前面带有x
的数字,以表示处理器数量的倍数。要增加线程数,您必须同时更改numThreads
和maxThreads
。在您的情况下,将两者都设置为x2
应该有效。