如何为Scala ExecutionContexts配置关闭策略?

时间:2014-06-11 18:19:45

标签: java multithreading scala executioncontext

我最近在一台机器上遇到了一些奇怪的行为,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文档,但我找不到我要找的内容。

2 个答案:

答案 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

  1. scala.concurrent.context.minThreads强制执行最少数量的线程。默认为1
  2. scala.concurrent.context.numThreads设置线程数。默认为x1
  3. scala.concurrent.context.maxThreads强加最大线程数。默认为x1
  4. 这些中的每一个都可以是数字或前面带有x的数字,以表示处理器数量的倍数。要增加线程数,您必须同时更改numThreadsmaxThreads。在您的情况下,将两者都设置为x2应该有效。