让我们说我们有一个Akka演员,它以var
的形式维持一个内部状态。
class FooActor extends Actor {
private var state: Int = 0
def receive = { ... }
}
假设接收处理程序调用返回未来的操作,我们使用调度程序将其映射为上下文执行程序,最后我们设置一个改变actor状态的onSuccess
回调。
import context.dispatcher
def receive = {
case "Hello" => requestSomething() // asume Future[String]
.map(_.size)
.onSuccess { case i => state = i }
}
从onSuccess
回调改变actor的状态是否是线程安全的,即使使用actor调度程序作为执行上下文?
答案 0 :(得分:10)
不,不是(akka 2.3.4 documentation)。
在这种情况下,您需要做的是向自己发送消息以更改状态。如果您需要订购,您可以使用藏匿并成为。像这样的东西
import akka.actor.{Stash,Actor}
import akka.pattern.pipe
case class StateUpdate(i:int)
class FooActor extends Actor with Stash{
private var state: Int = 0
def receive = ready
def ready = {
case "Hello" => requestSomething() // asume Future[String]
.map(StateUpdate(_.size)) pipeTo self
become(busy)
}
def busy {
case StateUpdate(i) =>
state=i
unstashAll()
become(ready)
case State.Failure(t:Throwable) => // the future failed
case evt =>
stash()
}
}
当然这是一个简单的实现,您可能希望处理超时和东西以避免让您的演员卡住。
如果您不需要在州内订购保证:
case class StateUpdate(i:int)
class FooActor extends Actor with Stash{
private var state: Int = 0
def receive = {
case "Hello" => requestSomething() // asume Future[String]
.map(StateUpdate(_.size)) pipeTo self
case StateUpdate(i) => state=i
}
但是演员状态可能不是最后收到的字符串的长度
答案 1 :(得分:1)
只是为了支持Jean的答案,这里是文档的例子:
class MyActor extends Actor {
var state = ...
def receive = {
case _ =>
//Wrongs
// Very bad, shared mutable state,
// will break your application in weird ways
Future {state = NewState}
anotherActor ? message onSuccess {
r => state = r
}
// Very bad, "sender" changes for every message,
// shared mutable state bug
Future {expensiveCalculation(sender())}
//Rights
// Completely safe, "self" is OK to close over
// and it's an ActorRef, which is thread-safe
Future {expensiveCalculation()} onComplete {
f => self ! f.value.get
}
// Completely safe, we close over a fixed value
// and it's an ActorRef, which is thread-safe
val currentSender = sender()
Future {expensiveCalculation(currentSender)}
}
}