我知道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
}
如果我向此actor发送DoSomething消息,然后立即重复向此actor发送GetStatus,我将看到状态0,1和2按顺序返回。如果actor模型一次只处理一条消息,我只会看到状态2被返回,因为我无法访问中间状态。
似乎演员模式仍然需要锁定。我错过了什么?
答案 0 :(得分:5)
当你关闭一个actor的可变状态并将它暴露给其他线程时,所有的注意都会被关闭,这是你的代码在(嵌套的){{1}中变异status
时所做的事情。 }。 Akka documentation明确警告不要这样做。
演员一次处理一条消息:
Future
从同一发件人向上述演员发送var status = 0
def receive = {
case IncrementStatus =>
status += 1
case GetStatus =>
val s = status
sender ! s
}
,另一个IncrementStatus
,然后IncrementStatus
条消息将导致该发件人收到GetStatus
。
但是,尝试使用2
s做同样的事情并不能保证相同的结果,因为Future
是异步完成的。例如:
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
}
}
map
为Future
创建一个Future[UpdateStatus]
,然后pipe为行为者Future
的结果。
如果我们向同一位发件人发送IncrementStatusInFuture
,另一个IncrementStatusInFuture
,然后发送GetStatus
个MyActor
邮件,我们无法保证发件人会收到2
1}}。 actor按顺序处理这三个消息,但是当actor处理NumService.addOne
消息时,对GetStatus
的一个或两个调用可能尚未完成。这种不确定行为是Future
的特征;它并不违反一次一个消息处理的行为者原则。