为什么在演员中创建演员是危险的

时间:2017-06-14 06:06:23

标签: scala akka

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引用。但我无法理解(也无法找到任何例子)为什么这有害以及它可能导致什么问题?

3 个答案:

答案 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)
       ...
  }
}
对于没有采用构造函数参数的演员来说,它没有那么大的可能性,但是一旦参数发挥作用,你需要小心关闭内部状态。