Elixir:在主管内部获取流程的pid

时间:2018-04-18 15:51:25

标签: elixir

我有两种由主管启动的流程:一些工人和一个工人需要报告的控制器。为此,他们需要知道控制器的pid。

我正在考虑两种方法,但没有办法。

立即开始

原则是立即启动所有进程,然后检索控制器的pid,然后将其发送给所有工作人员,以便他们可以开始工作。代码如下所示:

defmodule XYZ.MySup do
  use Supervisor

  def start_link(opts) do
  ...
  end

  def init(:ok) do
    children = [
      {XYZ.Controller, name: :ctrl},

      %{id: XYZ.Worker_1,
        start: {XYZ.Worker,
                :start_link, [[name: :w1, args: {"arg_1"}]]}},

      ... some other workers ...
    ]

    Supervisor.init(children, strategy: :one_for_one)

    ch = Supervisor.which_children(self())
    ...

  end
end

Supervisor.which_children(self())行会生成错误** (EXIT) process attempted to call itself

首先启动控制器

原则是启动控制器,然后获取其pid,然后使用控制器的pid作为参数启动工作程序(这将是首选方法,顺便说一句)。

  1. 当然,获取控制器的pid会遇到同样的问题:运行时拒绝调用Supervisor.which_children(self))
  2. 即使可以解决这个问题,也可以通过以下方式向主管添加一名工人:

    Supervisor.start_child(self(),
      %{id: XYZ.Worker_3,
        start: {XYZ.Worker,
                :start_link, [[name: :w3, args: {"arg_3"}]]}})
    

    失败,错误为** (EXIT) process attempted to call itself

  3. 我做错了什么?

1 个答案:

答案 0 :(得分:2)

好的,所以当主管已处理消息时,您无法向主管发送消息。在这种情况下,它是start_link / init消息。像Elixir / Erlang中的所有内容一样,一切都是一个过程,一次只能处理一条消息。基本上通过在init调用中执行此操作,您将创建一个锁。这是因为在init函数成功退出并且主管是监督树的一部分之前,主管不会开始接受消息。这相当于在自己的handle_cast / handle_call回调中调用对GenServer的强制转换/调用。

您还希望使用:simple_one_for_one策略来动态启动工作人员。

接下来,您应该为您的主管命名,这样您就可以在不必获取pid的情况下调用它。

最后,您应该将启动功能包装在Application定义中的任务中。

就个人而言,我喜欢这样做:

defmodule MyApp.Supervisor do
  use Supervisor

  def start_link do
    # Name your supervisor `MyApp.Supervisor`
    Supervisor.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init([]) do
    children = [
      worker(MyApp.Worker, [])
    ]

    supervise(children, strategy: :simple_one_for_one)
  end
end

然后使用应用程序定义中的任务:

defmodule MyApp.Application do
  use Application

  def start(_, _) do
    import Supervisor.Spec, warn: false

    children = [
      supervisor(MyApp.WorkerSupervisor, []),
      worker(Task, [&on_boot/0], restart: :temporary)
    ]

    Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
  end

  defp on_boot do
    child_spec = MyApp.Worker.child_spec(args: [_arg1, _arg2, _arg3], name: :w1)
    Supervisor.start_child(MyApp.WorkerSupervisor, child_spec)
    # ... more workers
    :ok
  end
end

我不完全确定上面的child_spec来电,因此您可能需要查看相关文档:https://hexdocs.pm/elixir/Supervisor.html

然后,您可以通过在应用程序中的任何其他位置调用Supervisor.which_chldren(MyApp.WorkerSupervisor)来获取主管的孩子。

如果要查看生成的监督树,在iex控制台内使用:observer.start()并单击Applications选项卡总是有帮助的。