【发布时间】:2014-05-13 10:49:13
【问题描述】:
我正在尝试使用 Akka 角色的层次结构来处理每个用户状态。有一个父actor拥有所有子actor,并以正确的方式处理get-or-create(参见a1、a2):
class UserActorRegistry extends Actor {
override def Receive = {
case msg@ DoPerUserWork(userId, _) =>
val perUserActor = getOrCreateUserActor(userId)
// perUserActor is live now, but will it receive "msg"?
perUserActor.forward(msg)
}
def getOrCreateUserActor(userId: UserId): ActorRef = {
val childName = userId.toActorName
context.child(childName) match {
case Some(child) => child
case None => context.actorOf(Props(classOf[UserActor], userId), childName)
}
}
为了回收内存,UserActors 在一段空闲时间后过期(即计时器触发子actor调用context.stop(self))。
我的问题是我认为“getOrCreateUserActor”和接收转发消息的子 Actor 之间存在竞争条件 - 如果子 Actor 在该窗口中过期,则转发消息将丢失。
有什么方法可以检测到这种边缘情况,或者重构 UserActorRegistry 以排除它?
【问题讨论】:
-
如果您在地图中明确维护子角色列表,它会变得更简单。然后,您可以通过向父级发送到期消息来让子级actors 发出超时信号,这会导致父级停止子级并清理地图。地图是同步维护的(从父actor的角度来看),所以竞争条件消失了。
-
这听起来很明智。我还必须使用地图查找儿童演员,而不是按名称查找,否则可能会发生冲突,儿童预定死亡,但仍然存在于 Akka 的儿童地图中。当 Akka 已经有了这张地图时,维护一个单独的子地图感觉很浪费。
-
一个单独的地图似乎是多余的。考虑到我经常维护两个地图,第二个由 ActorRef 索引,以便在我收到 Terminated 时有效地清理第一个。是的,很有趣,使用 child() 会很巧妙。
-
“使用 child() 会很巧妙”——是的,这就是我想要做的,但它导致了这个问题中的竞争条件 :-(
标签: scala akka actor race-condition