我有两种由主管启动的流程:一些工人和一个工人需要报告的控制器。为此,他们需要知道控制器的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作为参数启动工作程序(这将是首选方法,顺便说一句)。
Supervisor.which_children(self))
。即使可以解决这个问题,也可以通过以下方式向主管添加一名工人:
Supervisor.start_child(self(),
%{id: XYZ.Worker_3,
start: {XYZ.Worker,
:start_link, [[name: :w3, args: {"arg_3"}]]}})
失败,错误为** (EXIT) process attempted to call itself
。
我做错了什么?
答案 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
选项卡总是有帮助的。