使用Futures的演员内的并发

时间:2015-03-20 04:40:44

标签: scala akka actor

我想知道是否有更好的方法来处理Actor中的值的异步初始化。当演员内部的演员当然是线程安全的,但使用Futures会引发一个皱纹(你必须确保你不要关闭contextsender)考虑以下事项:

class ExampleActor(ref1: ActorRef, ref2: ActorRef) extends Actor {

  implicit val ec = context.dispatcher

  val promise1 = Promise[Int]
  val promise2 = Promise[Int]

  def receive = {
    case Request1.Response(x) => promise1.success(x)
    case Request2.Response(y) => promise2.success(y)
    case CombinedResponse(x, y) => x + y
  }

  promise1.future foreach { x =>
    promise2.future foreach { y =>
      self ! CombinedResponse(x, y) 
    }
  }

  ref1 ! Request1
  ref2 ! Request2
}

是否有更好/更惯用的方式来处理这样的并行请求?

1 个答案:

答案 0 :(得分:2)

您实际上不需要期货来处理多部分响应:

var x: Option[Int] = None
var y: Option[Int] = None

def receive = {
  case Request1.Response(x) => x = Some(x); checkParts
  case Request2.Response(y) => y = Some(y); checkParts
}

def checkParts = for {
   xx <- x
   yy <- y
} parent ! xx + yy

顺便说一下,即使是期货,你也可以用同样的方式理解。

管理演员状态的更多功能方法:

case class Resp1(x: Int)
case class Resp2(y: Int)
case class State(x: Option[Int], y: Option[Int])

class Worker(parent: ActorRef) extends Actor {
  def receive = process(State(None, None))

  def process(s: State): Receive = edge(s) andThen { sn => 
    context become process(sn)
    for {
       xx <- sn.x
       yy <- sn.y
    } parent ! xx + yy //action
  }

  def edge(s: State): PartialFunction[Any, State] = { //managing state
    case Resp1(x) => s.copy(x = Some(x))
    case Resp2(y) => s.copy(y = Some(y))
  }

}

重用actor而不是创建未来更好,因为promise.success实际上通过将任务提交到执行程序来实现不可管理的副作用,所以它不是纯粹的功能方式。演员的状态更好,因为演员内部的副作用总是与其他演员一致 - 他们是逐步应用的,只是响应某些消息。所以你可能会在无限集合中看到演员fold;由actor发送的状态和消息(也是无限的)可以看作是fold的累加器。

谈到Akka,它的演员有一些IoC功能,比如自动异常处理(通过监督),这在未来是不可用的。在您的情况下,您必须引入一个额外的复合消息以返回到actor的IoC上下文中。添加除self ! CombinedResponse(x, y)之外的任何其他操作(例如,某些其他开发人员可能会意外地执行某些解决方法)是不安全的。