Можно ли поймать внешний выход в Эрланге?
У меня есть два связанных процесса; скажем, они A
а также B
, с A
установить ловушку выходов. Я хочу быть в состоянии восстановить часть B
обрабатывать данные, если кто-то звонит exit/2
на нем, например exit(B, diediedie)
,
В B
модуль, назовем его bmod.erl
У меня есть код, который выглядит следующим образом:
-module(bmod).
-export([b_start/2]).
b_start(A, X) ->
spawn(fun() -> b_main(A, X) end).
b_main(A, X) ->
try
A ! {self(), doing_stuff},
do_stuff()
catch
exit:_ -> exit({terminated, X})
end,
b_main(A, X).
do_stuff() -> io:format("doing stuff.~n",[]).
И в A
модуль, назовем его amod.erl
У меня есть код, который выглядит следующим образом:
-module(amod).
-export([a_start/0]).
a_start() ->
process_flag(trap_exit, true),
link(bmod:b_start(self(), some_stuff_to_do)),
a_main().
a_main() ->
receive
{Pid, doing_stuff} ->
io:format("Process ~p did stuff.~n",[Pid]),
exit(Pid, diediedie),
a_main();
{'EXIT', Pid, {terminated, X}} ->
io:format("Process ~p was terminated, had ~p.~n", [Pid,X]),
fine;
{'EXIT', Pid, _Reason} ->
io:format("Process ~p was terminated, can't find what it had.~n", [Pid]),
woops
end.
(Я понимаю, что я должен сделать spawn_link
Обычно, но в моей исходной программе есть код между порождением и ссылкой, и я смоделировал этот пример кода таким образом.)
Теперь, когда я запускаю код, я получаю это.
2> c(amod).
{ok,amod}
3> c(bmod).
{ok,bmod}
4> amod:a_start().
doing stuff.
Process <0.44.0> did stuff.
doing stuff.
Process <0.44.0> did stuff.
Process <0.44.0> was terminated, can't find what it had.
woops
5>
Как я могу получить b_main()
поймать этот внешний выход, чтобы он мог сообщить о своем состоянии X
?
2 ответа
За b_main()
чтобы поймать внешний выход, он должен перехватить выход, вызвав process_flag(trap_exit, true)
, Это приведет к сообщению в процесс, где он может выйти с состоянием X
, Код как ниже
b_start(A, X) ->
spawn(fun() -> process_flag(trap_exit, true), b_main(A, X) end).
b_main(A, X) ->
try
A ! {self(), doing_stuff},
do_stuff()
catch
exit:_ ->
io:format("exit inside do_stuff() . ~n"),
exit({terminated, X})
end,
receive
{'EXIT',Pid, Reason} ->
io:format("Process received exit ~p ~p.~n",[Pid, Reason]),
exit({terminated, X})
after 0 ->
ok
end,
b_main(A, X).
Краткий ответ: вы должны сделать trap_exit
в b_main/2
а так и получи {'EXIT', ...}
Сообщения. Это было изложено @vinod прямо перед моей попыткой. Вместо этого я попытаюсь объяснить кое-что о том, что происходит.
Если процесс перехватывает выходы и он умирает, например, когда кто-то звонил exit(Pid, die)
или какой-то связанный процесс заканчивается exit(die)
тогда он получит {'EXIT', ...}
сообщение в свой почтовый ящик вместо того, чтобы молча умереть по той же причине. Это система времени выполнения, которая выдает сигналы на выход каждому связанному процессу, и его можно перехватить вместо смерти.
Единственное исключение из этого правила - это когда exit(Pid, kill)
вызов выполнен, тогда независимо от того, перехватывает ли процесс выходы или нет, он просто умирает с разумом kill
,
Таким образом, чтобы избежать тихой смерти, вызванной внешним сигналом выхода, процесс должен перехватывать выходы. Опять же, если процесс хочет знать, почему кто-то, связанный с ним, только что умер, и предпринять некоторые усилия, чтобы восстановиться, этот процесс должен перехватить выходы. Каждый захваченный сигнал выхода появляется как сообщение в почтовом ящике процесса.
Таким образом, нет никакого эффекта от вашего try ... catch exit:_ -> ...
заявление в вопросе улавливания выходов.
В общем-то trap_exit
считается плохой практикой. Есть простой пример, который показывает, почему:
18> self().
<0.42.0>
19> Pid = spawn_link(fun () -> process_flag(trap_exit, true),
Loop = fun (F) -> receive Any -> io:format("Any: ~p~n", [Any]) end, F(F) end,
Loop(Loop) end).
<0.58.0>
20> exit(Pid, grenade).
Any: {'EXIT',<0.42.0>,grenade}
true
21> exit(Pid, grenade).
Any: {'EXIT',<0.42.0>,grenade}
true
...
Как вы можете видеть, какой-то процесс связан, перехватывает выходы и отказывается выходить нормально. Это неожиданно и, очевидно, потенциально опасно. И это может нарушить цепочку выходов, созданных для набора связанных процессов, поскольку ссылки являются транзитивными.
Есть множество тонких особенностей, которые чудесно изложены в этой главе книги.