【问题标题】:Erlang freezes on supervisor:start_childErlang 在主管上冻结:start_child
【发布时间】:2013-10-05 13:02:09
【问题描述】:

我想用一个进程启动一个主管,该进程会产生更多链接到主管的进程。程序在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 控制台。我没有收到任何错误或任何其他消息。有任何想法吗?我是否正确启动孩子?

【问题讨论】:

  • 如果您不从mainapp:init 内部调用mainapp:start_child,而是在supervisormainapp 启动后从shell 提示符手动调用,会发生什么情况?我是 Erlang 的新手,但我怀疑这是因为 gen_server:start_link 是同步的,并且在它运行时,gen_server 没有收到消息。我认为supervisor:start_child 是一个调用,它被转换为发送给主管的消息。请参阅gen_server 的文档,其中说call 是同步的。但我不确定这一切,因为我是新手。

标签: erlang


【解决方案1】:

当您启动子进程时,来自主管的调用将仅在子进程 init(如果子进程是 gen_server 则 start_link 被阻塞直到 init)返回后返回。您正在主管中启动主 gen_server。因此主管正在等待主应用程序返回。同时 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动态启动?
  • mainapp 和子进程管理器都可以由应用程序管理器启动。 example
猜你喜欢
  • 2017-01-10
  • 2020-08-02
  • 2013-03-21
  • 2017-02-21
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
  • 2011-08-07
  • 2012-11-16
相关资源
最近更新 更多