akka documentation清楚地表明在这样的演员中创建演员是危险的:
class ActorA extends Actor {
def receive = ???
}
final class ActorB extends Actor {
def receive = {
case _ =>
val act = context.actorOf(Props(new ActorA))
}}
我知道Actor的apply方法正在接受创建actor的this
引用。但我无法理解(也无法找到任何例子)为什么这有害以及它可能导致什么问题?
答案 0 :(得分:3)
让我们稍微调整你的例子
class ActorA(str:String) extends Actor {
def receive = ???
}
final class ActorB extends Actor {
def receive = {
case _ =>
val act = context.actorOf(Props(new ActorA("hidden")))
}}
使用actor的大多数常见用例是处理故障转移和监督,当一个actor失败并需要重新启动时,actor系统需要知道如何做到这一点。当你使用道具(道具(新的ActorA))时,你已经通过自己处理隐藏了“隐藏”的参数值。
如果相反,您声明如何创建actor的实例,而不是这样做,演员系统将确切地知道在重新创建actor时需要做什么 - 即使用构造函数参数“hidden”创建一个ActorA实例。
即使你的没有参数的演员的例子
context.actorOf(Props(new ActorA))
不推荐这种在另一个actor中实例化actor的方法,因为它鼓励关闭封闭范围,导致不可序列化的Props和可能的竞争条件(打破actor封装)。
答案 1 :(得分:0)
我认为我们混淆了创作和宣言。医生说
Declaring one actor within another is very dangerous and breaks actor encapsulation. Never pass an actor’s this reference into Props!
所以问题是宣言,而不是创造! 我们来看看Java:
public class MyActor extends AbstractActor {
@Override
public Receive createReceive() {
return ReceiveBuilder.create()
.match(String.class, handleString())
.matchAny(x -> unhandled(x))
.build();
}
private FI.UnitApply<String> handleString() {
return message -> sender().tell("OK", getSelf());
}
class MyOtherActor extends AbstractActor {
@Override
public Receive createReceive() {
return ReceiveBuilder.create()
.match(String.class, handleString())
.matchAny(x -> unhandled(x))
.build();
}
private FI.UnitApply<String> handleString() {
return message -> sender().tell("OK-Inner", getSelf());
}
}
}
现在,如果MyOtherActor是普通类,我们只能从MyActor实例中实例化它:
MyActor actor = new MyActor();
MyActor.MyOtherActor otherActor = actor.new MyOtherActor();
这意味着MyOtherActor的构造函数依赖于MyActor的实例!
现在,如果道具应该包含演员的“工厂”。他们需要一种工厂方法。如果我们的MyOtherActor声明为我们在这里所做的那样,那么我们的道具就像这样(ish):
MyActor actor = ??? // how did you even get a reference to the actor and not the actorRef in the first place!
Props otherActorProps = Props.create(MyActor.MyOtherActor.class, () -> actor.new MyOtherActor());
砰的一声,这是踢球者!现在,您的otherActorProps
包含对actor
的引用,即您已关闭可变状态!如果由于某种原因actor
“死亡”,你的道具仍然会引用它,造成各种奇怪。
还有一个问题是如何首先获得对actor
的引用,而不是actorRef
恕我直言,这就是文档所指的内容,而不是“创造”(即实例化,产生)另一个中的一个演员的事实:这是绝对正常的,这是akka的常规操作(这就是为什么你可以做{ {1}}以及getContext().actorOf(..)
答案 2 :(得分:0)
文档中有警告,因为它很容易意外地关闭创建actor的状态,包括它的this
指针(你不应该在基于actor的代码中使用它)
根据我的经验,我经常看到将props
方法放入演员的伴侣对象中:
object ActorA {
def props() = Props(new ActorA)
}
这样做可以确保返回的Props
不会关闭演员的状态。
class ActorB extends Actor {
def receive = {
case _ =>
val actorB = context.actorOf(ActorA.props)
...
}
}
对于没有采用构造函数参数的演员来说,它没有那么大的可能性,但是一旦参数发挥作用,你需要小心关闭内部状态。