我有50,000个任务,想要用10个线程执行它们。 在Java中我应该创建Executers.threadPool(10)并传递runnable然后等待处理所有。据我所知,Scala对该任务特别有用,但我无法在docs中找到解决方案。
答案 0 :(得分:58)
最简单的方法是使用scala.concurrent.Future
类和相关的基础结构。 scala.concurrent.future
方法异步计算传递给它的块,并立即返回表示异步计算的Future[A]
。期货可以通过多种非阻塞方式进行操作,包括映射,flatMapping,过滤,恢复错误等。
例如,这是一个创建10个任务的示例,其中每个任务都会休眠任意数量的时间,然后返回传递给它的值的平方。
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
val tasks: Seq[Future[Int]] = for (i <- 1 to 10) yield future {
println("Executing task " + i)
Thread.sleep(i * 1000L)
i * i
}
val aggregated: Future[Seq[Int]] = Future.sequence(tasks)
val squares: Seq[Int] = Await.result(aggregated, 15.seconds)
println("Squares: " + squares)
在这个例子中,我们首先创建一系列单独的异步任务,完成后提供一个int。然后,我们使用Future.sequence
将这些异步任务合并到一个异步任务中 - 交换类型中Future
和Seq
的位置。最后,我们在等待结果的同时阻止当前线程长达15秒。在该示例中,我们使用全局执行上下文,该上下文由fork / join线程池支持。对于非平凡的示例,您可能希望使用特定于应用程序的ExecutionContext
。
通常,应尽可能避免阻塞。 Future
类上还有其他组合可以帮助以异步方式编程,包括onSuccess
,onFailure
和onComplete
。
另外,请考虑调查Akka库,它为Scala和Java提供基于actor的并发性,并与scala.concurrent
互操作。
这种最简单的方法是使用Scala的Future类,它是Actors框架的子组件。 scala.actors.Futures.future方法为传递给它的块创建一个Future。然后,您可以使用scala.actors.Futures.awaitAll等待所有任务完成。
例如,这是一个创建10个任务的示例,其中每个任务都会休眠任意数量的时间,然后返回传递给它的值的平方。
import scala.actors.Futures._
val tasks = for (i <- 1 to 10) yield future {
println("Executing task " + i)
Thread.sleep(i * 1000L)
i * i
}
val squares = awaitAll(20000L, tasks: _*)
println("Squares: " + squares)
答案 1 :(得分:16)
您想要查看Scala actor库或Akka。 Akka具有更清晰的语法,但要么会做到这一点。
所以听起来你需要创建一个知道如何处理任务的演员池。一个Actor基本上可以是任何带有receive方法的类 - 来自Akka教程(http://doc.akkasource.org/tutorial-chat-server-scala):
class MyActor extends Actor {
def receive = {
case "test" => println("received test")
case _ => println("received unknown message")
}}
val myActor = Actor.actorOf[MyActor]
myActor.start
您需要创建一个actor实例池,并将您的任务作为消息发送给它们。以下是关于Akka演员合并的帖子,可能会有所帮助:http://vasilrem.com/blog/software-development/flexible-load-balancing-with-akka-in-scala/
在您的情况下,每个任务一个actor可能是合适的(与线程相比,actor非常轻量级,因此您可以在一个VM中拥有很多),或者您可能需要在它们之间进行更复杂的负载平衡。
编辑: 使用上面的示例actor,向它发送消息就像这样简单:
myActor ! "test"
然后,演员将“接收测试”输出到标准输出。
消息可以是任何类型,当与Scala的模式匹配结合使用时,您可以使用强大的模式来构建灵活的并发应用程序。
一般来说,Akka演员将在线程共享方面做“正确的事情”,而对于OP的需求,我想默认是好的。但是如果需要,可以将actor应该使用的调度程序设置为以下几种类型之一:
* Thread-based
* Event-based
* Work-stealing
* HawtDispatch-based event-driven
为演员设置调度员是微不足道的:
class MyActor extends Actor {
self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("thread-pool-dispatch")
.withNewThreadPoolWithBoundedBlockingQueue(100)
.setCorePoolSize(10)
.setMaxPoolSize(10)
.setKeepAliveTimeInMillis(10000)
.build
}
请参阅http://doc.akkasource.org/dispatchers-scala
通过这种方式,您可以限制线程池大小,但同样,使用默认调度程序的50K Akka actor实例可能会满足原始用例,并且它可以很好地并行化。
这实际上只是触及了Akka可以做的事情。它带来了许多Erlang为Scala语言提供的功能。 Actor可以监视其他actor并重新启动它们,从而创建自我修复的应用程序。 Akka还提供软件事务内存和许多其他功能。它可以说是Scala的“杀手级应用”或“杀手级框架”。
答案 2 :(得分:8)
如果你想“用10个线程执行它们”,那么使用线程。 Scala的演员模型,当他们说Scala有利于并发时,通常就是人们所说的,隐藏这样的细节,所以你不会看到它们。
使用actor或者你拥有的所有东西都是简单的计算,你只需要创建50000个并让它们运行。您可能面临问题,但它们具有不同的性质。
答案 3 :(得分:8)
这是另一个类似于mpilquist的回答的答案,但没有弃用的API,包括通过自定义ExecutionContext的线程设置:
import java.util.concurrent.Executors
import scala.concurrent.{ExecutionContext, Await, Future}
import scala.concurrent.duration._
val numJobs = 50000
var numThreads = 10
// customize the execution context to use the specified number of threads
implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(numThreads))
// define the tasks
val tasks = for (i <- 1 to numJobs) yield Future {
// do something more fancy here
i
}
// aggregate and wait for final result
val aggregated = Future.sequence(tasks)
val oneToNSum = Await.result(aggregated, 15.seconds).sum