当我在集合上调用par
时,似乎创建了大约5-10个线程,这对于CPU绑定的任务很好。
但是有时候我有一些任务是与IO绑定的,在这种情况下,我希望同时从IO中提取500-1000个线程-做10-15个线程非常慢,而且我看到我的CPU大部分处于空闲状态。 >
我该如何实现?
答案 0 :(得分:3)
您可以将阻塞的io操作包装在blocking
块中:
(0 to 1000).par.map{ i =>
blocking {
Thread.sleep(100)
Thread.activeCount()
}
}.max // yield 67 on my pc, while without blocking it's 10
但是您应该问自己一个问题,是否应该将并行集合用于IO操作。他们的用例是执行CPU繁重的任务。
我建议您考虑使用IO调用期货。
您还应该考虑为该任务使用自定义执行上下文,因为全局执行上下文是公共单例,并且您无法控制使用什么代码以及出于何种目的。如果您使用外部库中的所有线程,则很容易饿死外部库创建的并行计算。
// or just use scala.concurrent.ExecutionContext.Implicits.global if you don't care
implicit val blockingIoEc: ExecutionContextExecutor = ExecutionContext.fromExecutor(
Executors.newCachedThreadPool()
)
def fetchData(index: Int): Future[Int] = Future {
//if you use global ec, then it's required to mark computation as blocking to increase threads,
//if you use custom cached thread pool it should increase thread number even without it
blocking {
Thread.sleep(100)
Thread.activeCount()
}
}
val futures = (0 to 1000).map(fetchData)
Future.sequence(futures).onComplete {
case Success(data) => println(data.max) //prints about 1000 on my pc
}
Thread.sleep(1000)
编辑
还可以通过 ForkJoinTaskSupport 使用自定义的 ForkJoinPool :
import java.util.concurrent.ForkJoinPool //scala.concurrent.forkjoin.ForkJoinPool is deprecated
import scala.util.Random
import scala.collection.parallel
val fjpool = new ForkJoinPool(2)
val customTaskSupport = new parallel.ForkJoinTaskSupport(fjpool)
val numbers = List(1,2,3,4,5).par
numbers.tasksupport = customTaskSupport //assign customTaskSupport