Erlang冻结了主管:start_child

时间:2013-10-04 17:11:52

标签: erlang

我想创建一个主管,其流程会产生更多与主管相关联的流程。该程序冻结在supervisor:start_child

主管启动主要孩子:

% supervisor (only part shown)

init([]) ->
    MainApp = ?CHILD_ARG(mainapp, worker, [self()]),
    {ok, { {one_for_one, 5, 10}, [MainApp]} }.

主要孩子从这里开始:

% 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控制台。我没有收到任何错误或任何其他消息。有任何想法吗?我能正确启动孩子吗?

1 个答案:

答案 0 :(得分:5)

当您启动子进程时,来自超级用户的调用将仅在子进程init之后返回(如果子进程是gen_server,则返回start_link被阻止直到init)。您正在主管中启动主gen_server。因此,主管正在等待mainapp返回。与此同时,mainapp正在调用supervisor:start_child函数。这会被阻止,因为主管正在等待从mainapp返回。这会导致死锁情况。

一种可能的解决方案是不要在mainapp中调用start_child并在init返回后异步执行

为此,您可以向自己发送演员信息,以便您可以启动孩子。或者您可以生成另一个启动并将响应(子Pid)发送到mainapp的进程

init([SuperPid]) ->
    handle_cast(self(), {start, SuperPid}),   % <-- send a cast message to itself
    {ok, #state{sup=SuperPid}}.

另一个优选的解决方案是拥有监督树。子进程可以有自己的主管,mainapp会调用孩子的主管来启动子进程。