并行执行期货清单

时间:2015-01-12 14:44:09

标签: scala akka future

我对Scala的Future和Akka非常陌生,目前,我正在尝试实施一个执行独立任务列表并将结果收集在一起的应用程序。

例如,我想要一个由多个任务组成的应用程序,每个任务接收一个数字,睡眠几秒钟,然后返回" Hello"消息。

演员实施如下:

class HelloActor extends Actor {
  def receive = {
    case name:Int => {
      println("%s will sleep for %s seconds".format(name, name % 4))
      Thread.sleep(name % 4 * 1000)
      sender ! "Hello %s".format(name)
    }
  }
}

主要对象实现为:

object HelloAkka extends App {
  val system = ActorSystem("HelloSystem")

  val helloActor = system.actorOf(Props[HelloActor], name = "helloactor")

  implicit val timeout = Timeout(20, TimeUnit.SECONDS)

  val futures = (1 to 10).map(num => {
    helloActor ? num
  })

  val future = Future.sequence(futures)

  val results = Await.result(future, timeout.duration)

  println(results)

  system.shutdown
}

由于每个任务将休眠0,1,2或3秒,我希望首先执行具有较短睡眠的任务。但结果是:

1 will sleep for 1 seconds
2 will sleep for 2 seconds
3 will sleep for 3 seconds
4 will sleep for 0 seconds
5 will sleep for 1 seconds
6 will sleep for 2 seconds
7 will sleep for 3 seconds
8 will sleep for 0 seconds
9 will sleep for 1 seconds
10 will sleep for 2 seconds
Vector(Hello 1, Hello 2, Hello 3, Hello 4, Hello 5, Hello 6, Hello 7, Hello 8, Hello 9, Hello 10)

换句话说,所有任务都按顺序执行。我想知道我是否有办法并行执行所有任务。

3 个答案:

答案 0 :(得分:4)

正如评论中所提到的,您将所有任务/消息发送给一个actor,并且保证所有这些任务/消息将按顺序处理。

要对任务进行并行处理,您需要拥有处理程序actor的多个实例,在您的情况下为HelloActor

当然你可以创建HelloActor的多个实例,但这绝对不是好的做法。

对于此类任务,您应该使用内置路由功能,该功能允许您管理工作人员/处理程序池并通过一个router参与者与他们进行交互,例如

val router: ActorRef =
  context.actorOf(RoundRobinPool(10).props(Props[HelloActor]), "router")

...
router ? num
...

请按照Akka Routing文档获取更多详细信息。

答案 1 :(得分:1)

我建议在Future执行实际任务,而不是像评论和答案中所建议的那样启动多个参与者。所以你的演员更像是任务的协调员。 E.g:

//...    

// import pipe pattern to get access to `pipeTo` method
import akka.pattern.pipe
import scala.concurrent.Future

// the `Future`s will be executed on this dispatcher
// depending on your needs, you may want to create a 
// dedicated executor for this
class TaskCoordinatorActor extends Actor {
  import context.dispatcher

  def receive = {
    case name: Int =>
      Future {
        Thread.sleep(name % 4 * 1000)
        "Hello %s".format(name)
      } pipeTo sender()
  }
}

上述代码在scala.concurrent.Future中执行您的任务,并将结果传递给原始发件人。这样,在创建Future之后,actor才会在任务完成之前阻塞,但是准备好接收下一条消息。

P.S。:而不是发送普通整数,你应该创建消息类型,明确你希望演员做什么。在你的情况下,它可以是:

case class Sleep(duration: Duration)

答案 2 :(得分:1)

从同一个actor发送到同一个actor的消息将按顺序执行。

你有两种选择。

为每条消息创建一个HelloActor的新副本,以便它们全部并行执行,或者将HelloActor修改为如下所示(可能是错误的导入,按内存进行):

import akka.pattern.pipe._

class HelloActor extends Actor {
  def receive = {
    case name:Int => {
      println("%s will sleep for %s seconds".format(name, name % 4))
      Future(sleepAndRespond(name)) pipeTo sender
   }
 }

 def sleepAndRespond(name:String) = {
   Thread.sleep(name % 4 * 1000)
   "Hello %s".format(innerName)
 }
}

这样,执行的顺序部分就是未来的管道,然后为十条消息中的每条消息异步执行。