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
}
}
}
在这种情况下,您不会 运行 进入任何竞争条件,尽管正如您所指出的,通常以这种方式构建代码并不是一个特别好的主意。
对 url
和 text
的引用都很好。 url
的值只是成功完成的 Future 的提取,并且不会改变您是否在 Actor 中。 text
的值是一个不可变的字符串,在 Future 中关闭该值应该不会导致问题,因为该字符串实例是不可变的。
正如您所指出的关闭 sender
或 var 是一个问题,那是因为作为可变对象,这些值可能会在 Future 完成时发生变化,这与不可变值不同,不可变值即使您关闭它们也会保持不变.
参考text
应该没问题。不同之处在于,text
的每个 "instance" 都是绑定到当前匹配块范围(从 case text ...
开始)的新变量。因此,创建的 Future
可以很好地关闭 text
的值。
这不同于 sender
(或脱糖时的 sender()
),后者实际上是定义在 Actor
特性上的方法,returns [=19] =] 是调用它的 actor 收到的最新消息的发送者,因此可以在稍后调用时给出不同的值(当 Future
的 onComplete
最终被调用时)。
您也对 onComplete
的使用产生怀疑。更好的选择是:
case text: String =>
Future {
fileUpload(text)
} map { url =>
Insert(url, text)
} pipeTo b
现在失败也会被发送到 b
,而不是被悄悄吞噬。
在重构其他一些程序员编写的 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
}
}
}
在这种情况下,您不会 运行 进入任何竞争条件,尽管正如您所指出的,通常以这种方式构建代码并不是一个特别好的主意。
对 url
和 text
的引用都很好。 url
的值只是成功完成的 Future 的提取,并且不会改变您是否在 Actor 中。 text
的值是一个不可变的字符串,在 Future 中关闭该值应该不会导致问题,因为该字符串实例是不可变的。
正如您所指出的关闭 sender
或 var 是一个问题,那是因为作为可变对象,这些值可能会在 Future 完成时发生变化,这与不可变值不同,不可变值即使您关闭它们也会保持不变.
参考text
应该没问题。不同之处在于,text
的每个 "instance" 都是绑定到当前匹配块范围(从 case text ...
开始)的新变量。因此,创建的 Future
可以很好地关闭 text
的值。
这不同于 sender
(或脱糖时的 sender()
),后者实际上是定义在 Actor
特性上的方法,returns [=19] =] 是调用它的 actor 收到的最新消息的发送者,因此可以在稍后调用时给出不同的值(当 Future
的 onComplete
最终被调用时)。
您也对 onComplete
的使用产生怀疑。更好的选择是:
case text: String =>
Future {
fileUpload(text)
} map { url =>
Insert(url, text)
} pipeTo b
现在失败也会被发送到 b
,而不是被悄悄吞噬。