Actor 和 Future:引用 onComplete 中的 actor 消息

Actor and Future: Referring to an actor message within onComplete

在重构其他一些程序员编写的 actor 代码时,我遇到了在 actor A 中使用 Future.onComplete 回调的情况,这与使用 akka.pattern.pipe 的最佳实践背道而驰。这是一个坏主意,因为它暴露了竞争条件的可能性,因为 Future 实例可能在不同的线程上执行。

查看代码,我们看到在 onComplete 块中既没有 sender 也没有任何可变的 var 被引用,所以它看起来很安全,至少对于 这个特定的场合。然而,一个让我疑惑的灰色区域是对 url 的引用,尤其是 text.

是否有可能类似于Closing Over An Akka Actor Sender In The Receive问题,发生竞争条件,使得在调用onComplete回调时,text的值已经指向一个不同的演员信息,导致一切都乱套了?

class B extends akka.actor.Actor {
  def receive = {
    case urlAndText: (String, String) => // do something
  }
}

class A extends akka.actor.Actor {
  case class Insert(url: String)

  def fileUpload(content: String): String = ??? // returns the url of the uploaded content
  val b = context.actorOf(Props(classOf[B]))
  def receive = {
    case text: String =>
      Future {
        fileUpload(text)
      } onComplete {
        case Success(url) =>
          b ! Insert(url, text) // will this be
      }
  }
}

在这种情况下,您不会 运行 进入任何竞争条件,尽管正如您所指出的,通常以这种方式构建代码并不是一个特别好的主意。

urltext 的引用都很好。 url 的值只是成功完成的 Future 的提取,并且不会改变您是否在 Actor 中。 text 的值是一个不可变的字符串,在 Future 中关闭该值应该不会导致问题,因为该字符串实例是不可变的。

正如您所指出的关闭 sender 或 var 是一个问题,那是因为作为可变对象,这些值可能会在 Future 完成时发生变化,这与不可变值不同,不可变值即使您关闭它们也会保持不变.

参考text应该没问题。不同之处在于,text 的每个 "instance" 都是绑定到当前匹配块范围(从 case text ... 开始)的新变量。因此,创建的 Future 可以很好地关闭 text 的值。

这不同于 sender(或脱糖时的 sender()),后者实际上是定义在 Actor 特性上的方法,returns [=19] =] 是调用它的 actor 收到的最新消息的发送者,因此可以在稍后调用时给出不同的值(当 FutureonComplete 最终被调用时)。

您也对 onComplete 的使用产生怀疑。更好的选择是:

case text: String =>
  Future {
    fileUpload(text)
  } map { url => 
    Insert(url, text) 
  } pipeTo b

现在失败也会被发送到 b,而不是被悄悄吞噬。