actor同时处理多条消息

Eri*_*rix 0 scala akka

我知道actor模型的一个优点是,通过一次只处理一条消息,简化了并发问题.但在我看来,我的演员正在处理多条消息.我有伪代码

var status = 0
def receive = {
  case DoSomething => 
    val dest = sender()
    status = 0

    for {
      otherActor <- resolveOtherActor("/user/OtherActor")
    } yield {
      for {
      res <- {status = 1
              otherActor ? doSomething1
              }
      res <- {status = 2
              otherActor ? doSomething2
              }
      } yield {
        dest ! status
      }
    }

  case GetStatus => sender() ! status
}
Run Code Online (Sandbox Code Playgroud)

如果我向此actor发送DoSomething消息,然后立即将GetStatus重复发送给此actor,我将看到状态0,1和2按顺序返回.如果actor模型一次只处理一条消息,我只会看到状态2被返回,因为我无法访问中间状态.

似乎演员模式仍然需要锁定.我错过了什么?

Jef*_*ung 5

当你关闭一个actor的可变状态并将它暴露给其他线程时,所有的注意都是关闭的,这是你的代码status在(嵌套的)内部变异时正在做的事情Future.Akka 文件明确警告不要这样做.

演员一次处理一条消息:

var status = 0
def receive = {
  case IncrementStatus =>
    status += 1
  case GetStatus =>
    val s = status
    sender ! s
}
Run Code Online (Sandbox Code Playgroud)

发送IncrementStatus另一个IncrementStatus,然后GetStatus从同一发件人发送到上述参与者的消息将导致该发送者接收2.

但是,尝试用Futures 做同样的事情并不能保证相同的结果,因为a Future是异步完成的.例如:

object NumService {
  // calculates arg + 1 in the future
  def addOne(arg: Int): Future[Int] = {
    Future { arg + 1 } 
  }
}

class MyActor extends Actor {
  var status = 0
  def receive = {
    case IncrementStatusInFuture =>
      val s = status
      NumService.addOne(s)
                .map(UpdateStatus(_))
                .pipeTo(self)

    case UpdateStatus(num) =>
      status = num

    case GetStatus =>
      val s = status
      sender ! s
  }
}
Run Code Online (Sandbox Code Playgroud)

我们mapFuture创建一个Future[UpdateStatus],然后管道到演员本身的结果Future.

如果我们向同一发件人发送IncrementStatusInFuture另一封邮件IncrementStatusInFuture,然后发送邮件,我们无法保证发件人会收到邮件.actor按顺序处理这三个消息,但是当actor处理消息时,一个或两个调用可能尚未完成.这种不确定行为是一个特征; 它不违反一次一个消息处理的行为者原则.GetStatusMyActor2NumService.addOneGetStatusFuture