我知道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被返回,因为我无法访问中间状态.
似乎演员模式仍然需要锁定.我错过了什么?
当你关闭一个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)
我们map将Future创建一个Future[UpdateStatus],然后管道到演员本身的结果Future.
如果我们向同一发件人发送IncrementStatusInFuture另一封邮件IncrementStatusInFuture,然后发送邮件,我们无法保证发件人会收到邮件.actor按顺序处理这三个消息,但是当actor处理消息时,一个或两个调用可能尚未完成.这种不确定行为是一个特征; 它不违反一次一个消息处理的行为者原则.GetStatusMyActor2NumService.addOneGetStatusFuture