为什么我应该使用context.become()在Actor中存储内部状态?

时间:2017-12-01 08:31:05

标签: scala akka actor

我已阅读scala-best-practices并对" 5.2提出疑问。应该只使用context.become"在actor中改变状态。我不明白为什么使用var存储内部状态是如此糟糕。如果actor按顺序执行所有消息,我就无法看到任何问题来源。我错过了什么?

2 个答案:

答案 0 :(得分:6)

考虑您引用的article中的第一个示例:

class MyActor extends Actor {
  val isInSet = mutable.Set.empty[String]

  def receive = {
    case Add(key) =>
      isInSet += key

    case Contains(key) =>
      sender() ! isInSet(key)
  }
}

这个例子没有任何本质上的错误,所以没有一些重要的理解,你错过了。如你所知,一个actor按顺序处理其邮箱中的消息,因此 可以安全地在一个内部变量中表示它的状态,并且只要该actor没有&#39>就会改变这个状态。 ; t暴露该状态 1

become通常用于动态切换演员的行为(例如,改变演员处理的消息类型)和/或其状态。在本文的第二个例子中,演员的状态在其行为中被编码为参数。这是第一个例子的优雅替代方案,但在这个简单的例子中,它是一个偏好的问题。

然而,become能够真正发挥作用的一种情况是具有许多状态转换的演员。如果不使用become,其receive块中的参与者逻辑可能会无法管理地变大或变成if-else语句的丛林。作为使用become建模状态转换的示例,请查看为sample project问题建模的"Dining Philosophers"

1 一个潜在的问题是虽然isInSet是一个val,但它是一个可变的Set,所以如果演员可能会发生奇怪的事情将此状态暴露给actor本身之外的东西(在示例中它没有做到)。例如,如果actor在消息中将此Set发送给另一个actor,则外部actor可以更改此状态,从而导致意外行为或竞争条件。可以通过将val更改为var,将可变设置更改为不可变Set来缓解此问题。

答案 1 :(得分:2)

我认为在演员中使用var并不一定有任何问题,正是因为你提到的原因(请记住,这只适用于在{{1 ,即,如果您启动一个线程,或使用receive(...),即使它来自Future,该代码也不再按顺序执行。

但是,我个人更喜欢使用receive来控制状态,主要是因为它清楚地向我显示了演员中可以改变的状态(context.become(...) s可以分散在各处。)

我还希望每处理一条消息将其限制为0或1次调用var,因此很清楚这种状态转换发生的位置。

也就是说,您可以通过使用约定来获得相同的好处,您可以在一个地方定义所有context.become(...),并确保在一个地方重新分配它们(比如说到最后)消息处理。