在Scala中最多只能执行4个并发期货

在Scala中最多只能执行4个并发期货,scala,concurrency,Scala,Concurrency,我认为使用期货可以很容易地让我发射一次性代码块,但似乎我一次只能有4个期货 这个限制是从哪里来的,还是我这样使用它滥用了未来 import scala.concurrent._ import ExecutionContext.Implicits.global import scala.util.{Failure, Success} import java.util.Calendar object Main extends App{ val rand = scala.util.Random

我认为使用期货可以很容易地让我发射一次性代码块,但似乎我一次只能有4个期货

这个限制是从哪里来的,还是我这样使用它滥用了未来

import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import java.util.Calendar

object Main extends App{

  val rand = scala.util.Random

  for (x <- 1 to 100) {
    val f = Future {
      //val sleepTime =  rand.nextInt(1000)
      val sleepTime =  2000
      Thread.sleep(sleepTime)

      val today = Calendar.getInstance().getTime()
      println("Future: " + x + " - sleep was: " + sleepTime + " - " + today)
      1;
    }
  }

  Thread.sleep(10000)
}
我希望他们都在同一时间出现


为了提供一些上下文,我想我可以使用这个构造并通过拥有一个主循环来扩展它,在主循环中,它根据从指数分布中提取的值休眠每个循环,以模拟用户到达/执行查询。每次睡眠后,我希望通过将查询发送到程序的驱动程序来执行查询(在本例中是Spark,驱动程序允许多个线程使用它)。有没有比使用Futures更明显的方法呢?

当您使用
导入ExecutionContext.Implicits.global
时, 它创建了与CPU数量大小相同的线程池

从源头上

默认的
ExecutionContext
实现由工作窃取线程池支持。默认情况下, 线程池使用的工作线程的目标数量等于[[可用处理器]]的数量

还有一个很好的问题:

由于线程池的默认大小取决于CPU的数量,如果您想使用更大的线程池,您必须编写如下代码

import scala.concurrent.ExecutionContext
import java.util.concurrent.Executors
implicit val ec = ExecutionContext.fromExecutorService(Executors.newWorkStealingPool(8))
implicit val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor)
在执行
未来操作之前

(在代码中,必须将其放置在循环的
之前。)

请注意,工作窃取池是在java 8中添加的,scala有自己的
ForkJoinPool
执行工作窃取:

另外,如果您希望将来每个
都有一个线程,您可以编写如下代码

import scala.concurrent.ExecutionContext
import java.util.concurrent.Executors
implicit val ec = ExecutionContext.fromExecutorService(Executors.newWorkStealingPool(8))
implicit val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor)
因此,下面的代码并行执行100个线程

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

object Main extends App{
  for (x <- 1 to 100) {
    implicit val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor)
    val f = Future {
      val sleepTime =  2000
      Thread.sleep(sleepTime)

      val today = Calendar.getInstance().getTime()
      println("Future: " + x + " - sleep was: " + sleepTime + " - " + today)
      1;
    }
  }

  Thread.sleep(10000)
}
导入scala.concurrent_
导入java.util.concurrent.Executors
对象主应用程序{

对于(x使用import scala.concurrent.ExecutionContext.Implicits.global
时的默认池,其线程数确实与计算机上的内核数相同。这非常适合于非阻塞代码(无同步io/sleep/…),但在使用它阻塞代码时可能会出现问题,甚至会导致死锁

但是,如果您在
scala.concurrent.blocking
块中标记阻塞代码,则此池实际上可能会增长。例如,当您使用
Await.result
Await.ready
函数在等待未来时阻塞时,使用相同的标记

有关详细信息,请参阅api文档

因此,您所要做的就是更新您的示例:

导入scala.concurrent.blocking
...
val睡眠时间=2000
阻塞{
线程。睡眠(睡眠时间)
}
...
现在所有期货将在2000毫秒后结束

您也可以使用

`implicit val ec = ExecutionContext.fromExecutorService(ExecutorService.newFixedThreadPool(NUMBEROFTHREADSYOUWANT))`
在NUMBEROFTHREADSYOUWANT中,您可以给出要启动的线程数。
这将在将来之前使用。

在后一种情况下,对于newSingleThreadExecutor,您仍然会遇到一个事实,即一个未来可以阻止下一个未来。这甚至比默认情况更糟。@hbogert您是否将
执行器放置在
未来之前。newSingleThreadExecutor
?我更新了这个问题,请看一看t、 它实际上创建了fork-join池,而不仅仅是修复-pool@ymonad是的,这似乎可行。但是,为每个线程初始化一个新的执行器似乎有点浪费(不过简单的实验表明并没有太大的区别)。但是,您对多个可用执行器的引用使我想到了CachedThreadPool,这在我的情况下是完美的。这并没有解决更深层次的问题。问题是非CPU密集型线程不应该占用完整的OS线程。在您的解决方案中,如果有100个非CPU密集型任务,则需要100个FixedThreadPool。