我想用一个进程启动一个主管,该进程会产生更多链接到主管的进程。程序在 supervisor:start_child
处卡住。
主管启动主要的 child :
% supervisor (only part shown)
init([]) ->
MainApp = ?CHILD_ARG(mainapp, worker, [self()]),
{ok, { {one_for_one, 5, 10}, [MainApp]} }.
主要的 child 从这里开始:
% mainapp (gen_server)
start_link([SuperPid]) when is_pid(SuperPid) ->
io:format("Mainapp started~n"),
gen_server:start_link({local, ?MODULE}, ?MODULE, [SuperPid], []).
init([SuperPid]) ->
{ok, _Pid} = start_child(childapp, SuperPid), % <-- here start the other
{ok, #state{sup=SuperPid}}.
start_child(Module, SuperPid) -> % Module = childapp
io:format("start child before~n"), % printed
ChildSpec = ?CHILD(Module, worker),
{ok, Pid} = supervisor:start_child(SuperPid, ChildSpec), % <-- here freezes
io:format("start child after~n"), % not printed
{ok, Pid}.
另一个子源包含
% childapp
start_link([]) ->
io:format("Child started~n"),
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%% gen_server interface
init([]) ->
{ok, #state{}}.
运行应用程序时我得到的输出是:
erl -pa ebin -eval "application:start(mysuptest)"
Erlang R16B01 (erts-5.10.2) [source-bdf5300] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V5.10.2 (abort with ^G)
1> Mainapp started
start child before
在这里它停止了 - 它卡住了,并且不会像往常一样返回到 erlang 控制台。我没有收到任何错误或任何其他消息。有任何想法吗?我是否正确地开始了 child ?
最佳答案
当你启动一个子进程时,来自supervisor的调用只会在子进程init(如果子进程是gen_server,start_link被阻塞直到init)返回后才会返回。您正在主管中启动主要的 gen_server。因此主管正在等待主应用程序返回。同时 mainapp 正在调用 supervisor:start_child 函数。这被阻止,因为主管正在等待从 mainapp 返回。这会导致死锁情况。
一种可能的解决方案是不要在 mainapp 中调用 start_child 并在 init 返回后异步执行
为此,您可以向其自身发送一条强制转换消息,您可以在其中启动子项。或者您可以生成另一个进程,该进程启动并将响应(子 Pid)发送到主应用程序
init([SuperPid]) ->
handle_cast(self(), {start, SuperPid}), % <-- send a cast message to itself
{ok, #state{sup=SuperPid}}.
另一个优选的解决方案是拥有一个监督树。子进程可以有自己的主管,主应用调用子进程的主管来启动子进程。
关于Erlang 在主管 :start_child 上卡住,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19187050/