Akka,期货和关键部分

时间:2014-08-08 08:18:17

标签: scala concurrency akka future

让我们说我们有一个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调度程序作为执行上下文?

2 个答案:

答案 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)}
    }
}