首页 > 解决方案 > 使用对象作为 AKKA 演员有什么危险?

问题描述

在下面的代码中,我使用的是 AKKA 演员 MonitorActor,即使它是一个对象。我从来没有在生产代码中看到过这种模式,尽管它似乎运行良好。

由于使用对象作为 Actor,以下代码是否存在并发问题?

这里是否有任何与 AKKA 演员相关的“陷阱”?

case class SomeEvent(member: String)

class Example(eventBus: EventBus)(implicit actorSystem: ActorSystem) {

  val members: AtomicReference[Set[String]] = new AtomicReference(Set())

  actorSystem.actorOf(Props(MonitorActor))

  private object MonitorActor extends Actor {

    eventBus.subscribe(classOf[SomeEvent])
    var isEnough = false

    override def receive: Receive = {
      case SomeEvent(member: String) =>
          val newMembers = members.updateAndGet(_ + member)
          if (newMembers.size >= 10) {
            isEnough = true
          }
        }
    }
}

标签: scalaakka

解决方案


这种“模式”引起的一个直接问题是:如果将Actor加到actorSystem两次会发生什么:

actorSystem.actorOf(Props(MonitorActor))
actorSystem.actorOf(Props(MonitorActor))

这不是一个微不足道的问题。在大型代码库中,可能存在多个文件/包,其中一个 Actor 被具体化,因此上述情况可能只是偶然出现。

充其量,每个SomeEvent都由完全相同的逻辑处理两次。在最坏的情况下,您将陷入令人讨厌的比赛条件isEnough。所以让我们假设最好的情况。

即使在最好的情况下,每个 SomeEvent 都将由完全相同的逻辑处理。members这在问题的示例中还不错,因为Set. 但是,如果它是 a List,您将开始获得同一事件的双重插入。

另一个问题是必须保护自己免受涉及members. members成为 an 的一个很好的理由AtomicReference是解决两个“独立”Actor 试图同时访问members的情况。但这违背了 Actor 模型的全部目的。从最初的 1973 年形式主义(强调我的):

该体系结构在控制结构方面是通用的,不需要或不需要 goto、interrupt 或semaphore 原语

在akka 文档的介绍中可以找到类似的描述(强调我的):

Actor 模型为编写并发和分布式系统提供了更高级别的抽象。它使开发人员不必处理显式锁定和线程管理,从而更容易编写正确的并发和并行系统。

所以我们有效地打破了 Actor 模型框架,我们得到的只是不必调用构造函数。将问题的示例代码与“首选”实现进行对比:

class MonitorActor() extends Actor { 

  val members: Set[String] = Set.empty[String]

  eventBus.subscribe(classOf[SomeEvent])
  var isEnough = false

  override def receive: Receive = {
    case SomeEvent(member: String) => {
      members add member
      isEnough = members.size >= 10
    }
  }
}

现在,开发人员不必担心信号量、竞争条件、线程争用……Actor 中的所有逻辑和功能都可以从串行的角度来理解。


推荐阅读