我的MyProcessingActor
actor将使用实体ID上的ConsistentHashingRouter
进行路由,这样任何时候(跨多个线程)都可能有多个此actor的实例正在进行中
演员使用地图来执行一些计算逻辑。因此,2个不同的actor实例可以同时读写这个地图。
在这些actor实例之间共享地图似乎公然违反了actor模型,即使它是Java ConcurrentHashMap
。
处理此类问题的最佳选择是什么?我没有在 Akka 文档中看到它,除非我错过了。
我可以看到两个选项:
MyMapManagerActor
的单个实例。如果这是单线程,那么有效MyProcessingActor
也将是单线程的还有其他推荐的方法吗?
答案 0 :(得分:2)
正如你在问题中所说,在演员之间分享可变状态是一件非常糟糕的事情。我使用了你提到的两种方法(使用类似MapManagerActor
和使用Akka STM的方法)并且两者都可以工作,尽管MapManagerActor
方法对我来说感觉更好。就MapManagerActor
而言,序列化的唯一部分是对Map的实际读取和写入,而不是计算本身,因此根据您的使用情况,您可能会发现并行化已足够。 / p>
另一个选择是用Actors替换Map本身。您可以拥有一个管理子项的父actor,以及每个Map条目中该Actor的一个子项。如果您的键组是静态的,则可以在启动时预先创建所有子Actors,例如使用键(或其哈希码)作为actor名称。然后,您可以使用actorSelection
直接访问子Actor,如/user/parentActor/<key>
中所示。如果需要动态创建actor,最好向父actor询问特定子项,并让它以ActorRef
回复给该特定子项(必要时创建它)。类似的东西:
override def receive = {
case GetActor(key) =>
context.children(key) match {
case Some(ref) => sender ! GetActorReply(ref)
case None => sender ! context.actorOf(ChildActor.props, key)
}
}
}
MyProcessingActor
可以使用ActorRef
进行所有处理。
答案 1 :(得分:0)
不幸的是,这在Akka中似乎是一件非常困难的事情。创建一个演员来管理对地图的访问证明非常麻烦,并且最新版本似乎不支持STM。
因此,这里的解决方案是仔细选择ConsistentHashingRouter
的密钥,以便在读取和写入地图时永远不会有任何竞争条件。有问题的地图是ConcurrentHashMap
答案 2 :(得分:0)
当Actor实际运行时,它在某个Executor上以Runnable
运行。按顺序运行一组合作参与者的技巧是为每个这样的一组参与者提供专用的串行执行器 - 参见my answer to the corresponding question。
答案 3 :(得分:-3)
如果地图是Actor实例上的一个字段,那么将有两个地图,不会出现交叉线程问题。如果地图是共享但只读,那么你仍然可以。如果共享地图并且两个actor都写入了它,那么你肯定需要使用ConcurrentHashMap进行锁定。