我正在用书学习erlang,在第13章中,第一个练习是编写一个函数my_spawn,它在生成的消息崩溃/退出时捕获退出消息。
-module(my_spawn1).
-compile(export_all).
my_spawn(Mod,Func,Args) ->
{M1, S1, Mi1} = os:timestamp(),
Pid = spawn(Mod,Func,Args),
lib_misc:on_exit(Pid, fun(Why) ->
{M2,S2,Mi2} = os:timestamp(),
ElapsedTime = (M2 - M1) * 1000000 + (S2 - S1) * 1000 + (Mi2-Mi1),
io:format("~p died with:~p~n consume time:~p(ms)", [Pid,Why,ElapsedTime]),
end),
Pid.
我的困惑是,如果在spawn_monitor之后,Mod:Func(Args)
已完成,但lib_misc:on_exit(...)
尚未设置,那么退出消息将会丢失,真的吗?
如果这是正确的,那么如何抓住这种情况呢?
[由Pascal编辑]
我添加了lib_misc的代码:on_exit / 2
on_exit(Pid, Fun) ->
spawn(fun() ->
process_flag(trap_exit, true), %% <label id="code.onexit1"/>
link(Pid), %% <label id="code.onexit2"/>
receive
{'EXIT', Pid, Why} -> %% <label id="code.onexit3"/>
Fun(Why) %% <label id="code.onexit4"/>
end
end).
答案 0 :(得分:1)
不,消息(与所有消息一样)将排队。但是,如果接收消息的进程消失,则其邮箱将丢失。
如果A使spawn_monitor
产生B,并且B立即死亡,则A保证接收DOWN消息。但是,如果A也死了,那么A的消息队列中的所有内容都将丢失。
答案 1 :(得分:1)
on_exit函数的第一件事就是生成一个将process_flag trap_exit设置为true的进程,因此它可以“防止”崩溃,并将接收类型为{'EXIT', Pid, Why}
的消息。
在下一行,它尝试将自己链接到Pid;有两种情况可能:
{'EXIT', Pid, noproc}
并将调用F(noproc)。{'EXIT', Pid, Reason}
并致电F(原因)。我不明白你为什么谈论spawn_monitor,它不会在你的情况下使用。无论如何,如果你在on_exit函数中用监视器(进程,Pid)替换链接(Pid),你甚至不需要使用trap_exit,因为如果Pid死了,监视器功能不会崩溃。在所有情况下,它都会返回消息{'DOWN',MonitorReference,process,Pid,Reason}
。 on_exit可以像这样修改:
on_exit(Pid, Fun) ->
spawn(fun() ->
MonitorReference = monitor(process,Pid),
receive
{'DOWN',MonitorReference,process,Pid,Why} -> Fun(Why)
end
end).