我想知道是否有更好的方法来处理Actor中的值的异步初始化。当演员内部的演员当然是线程安全的,但使用Futures会引发一个皱纹(你必须确保你不要关闭context
或sender
)考虑以下事项:
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
}
是否有更好/更惯用的方式来处理这样的并行请求?
答案 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)
之外的任何其他操作(例如,某些其他开发人员可能会意外地执行某些解决方法)是不安全的。