1

我有两个流程链接;假设它们是Aand B,并A设置为陷阱出口。B如果有人调用它,我希望能够恢复一段进程数据exit/2,例如exit(B, diediedie)

B's 模块中,我们称之为它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's 模块中,我们称之为它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

4

2 回答 2

3

为了b_main()捕获外部出口,它必须通过调用来捕获出口process_flag(trap_exit, true)。这将向进程发送一条消息,在该进程中它可以以 state 退出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).
于 2013-04-19T21:07:05.673 回答
2

简短的回答:你也应该这样做trap_exitb_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
...

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

本书章节中对许多微妙的专业进行了精彩的阐述。

于 2013-04-19T22:01:10.767 回答