2013-10-04 3 views
4

Я хочу запустить супервизора с процессом, который вызовет больше процессов, связанных с супервизором. Программа зависает на supervisor:start_child.Erlang зависает над супервизором: 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, как обычно. Я не получаю ошибок или других сообщений. Есть идеи? Правильно ли я начинаю ребенка?

+0

Что произойдет, если вы не вызываете 'mainapp: start_child' из' mainapp: init', но делаете это вручную из командной строки после запуска 'supervisor' и' mainapp'? Я новичок в Erlang, но я подозреваю, что это происходит потому, что gen_server: start_link является синхронным, и, хотя он работает, «gen_server» не получает сообщения. Я думаю, что 'supervisor: start_child' - это вызов, который преобразуется в сообщение, отправленное супервизору. См. Документацию для «gen_server», там сказано, что «вызов» является синхронным. Но я не уверен в этом, потому что я новичок в этом. – akonsu

ответ

5

Когда вы запускаете дочерний процесс, вызов супервизора возвращается только после инициализации дочернего процесса (в случае, если дочерний процесс является gen_server, start_link блокируется до init). Вы запускаете главный генератор gen_server в супервизоре. Следовательно, супервизор ждет возвращения mainapp. Между тем mainapp вызывает диспетчер: start_child. Это блокируется, потому что супервизор ждет возврата из mainapp. Это приводит к ситуации взаимоблокировки.

Одним из возможных решений является то, что не называют start_child в mainapp и сделать это асинхронно после инициализации возвращает

Для этого Вы можете отправить сообщение произнесения к себе, где вы можете начать ребенок. Или вы можете породить другой процесс, который начинается и отправляет ответ (ребенка Pid) к mainapp

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

Другим предпочтительным решением оказывает дерево надзора. У дочернего процесса может быть свой собственный супервизор, а mainapp вызывает супервизора ребенка для запуска дочернего процесса.

+0

В последнем случае второй супервизор (этот для дочерних процессов) также должен запускаться динамически с помощью 'mainapp'? –

+0

и mainapp, и диспетчер дочерних процессов могут запускаться супервизором приложения. [Пример] (http://learnyousomeerlang.com/building-applications-with-otp#a-pools-tree) – Vinod

Смежные вопросы