我可以在Erlang中找到一个外部出口吗?

时间:2013-04-19 20:46:57

标签: concurrency erlang try-catch exit

我有两个流程链接;假设它们是ABA设置为陷阱退出。如果有人在其上调用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,但在我原来的程序中,spawn和链接之间有代码,所以我用这种方式对这个代码建模。)

现在,当我运行代码时,我明白了。

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 个答案:

答案 0 :(得分:3)

要使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).

答案 1 :(得分:2)

简短回答:您也应该trap_exit b_main/2 {'EXIT', ...},并收到exit(Pid, die)条消息。在我尝试之前,@ edod概述了它。相反,我会尝试解释有关正在发生的事情的一些事情。

如果该过程正在捕获退出并且它会死亡,例如当有人呼叫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 ... 被视为不良做法。有一个简单的例子说明了原因:

{{1}}

正如您可能看到某些进程已关联,正在捕获退出并拒绝正常退出。这是出乎意料的,显然是有潜在危险的。并且它可能会破坏发布到一组链接进程的出口链,因为链接是可传递的。

在这个book chapter中有许多精妙的特色。