生成和接收的 erlang 竞争条件
erlang race condition of spawn and receive
我正在通过书本学习 erlang,在第 13 章中,第一个练习是编写一个函数 my_spawn,它在生成消息时捕获退出消息 crash/exited。
-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).
不,该消息(与所有消息一样)将被排队。但是,如果接收消息的进程终止,则其邮箱将丢失。
如果 A spawn_monitor
生成 B,B 立即死亡,A 肯定会收到 DOWN 消息。但是,如果 A 也死了,那么 A 的消息队列中的所有内容都将丢失。
函数 on_exit 做的第一件事是生成一个进程,将 process_flag trap_exit 设置为 true,因此它 "protected" 不会崩溃,并且会改为接收以下类型的消息:{'EXIT', Pid, Why}
.
在下一行它试图link自己到Pid;可能有 2 种情况:
- Pid 不存在(错误值,已经死了...)然后 on_exit 进程将收到消息
{'EXIT', Pid, noproc}
并将调用 F(noproc)。
- Pid 存在,然后 on_exit 进程将等待接收,直到进程 Pid 因某种原因死亡。 on_exit 将收到
{'EXIT', Pid, Reason}
并调用 F(Reason)。
我不明白你为什么说spawn_monitor,它没有用在你的情况下。无论如何,如果您在 on_exit 函数中将 link(Pid) 替换为 monitor(process,Pid) ,您甚至不需要使用 trap_exit 因为 monitor 函数不会崩溃如果 Pid 死了。在所有情况下,它 returns 消息 {'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).
我正在通过书本学习 erlang,在第 13 章中,第一个练习是编写一个函数 my_spawn,它在生成消息时捕获退出消息 crash/exited。
-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).
不,该消息(与所有消息一样)将被排队。但是,如果接收消息的进程终止,则其邮箱将丢失。
如果 A spawn_monitor
生成 B,B 立即死亡,A 肯定会收到 DOWN 消息。但是,如果 A 也死了,那么 A 的消息队列中的所有内容都将丢失。
函数 on_exit 做的第一件事是生成一个进程,将 process_flag trap_exit 设置为 true,因此它 "protected" 不会崩溃,并且会改为接收以下类型的消息:{'EXIT', Pid, Why}
.
在下一行它试图link自己到Pid;可能有 2 种情况:
- Pid 不存在(错误值,已经死了...)然后 on_exit 进程将收到消息
{'EXIT', Pid, noproc}
并将调用 F(noproc)。 - Pid 存在,然后 on_exit 进程将等待接收,直到进程 Pid 因某种原因死亡。 on_exit 将收到
{'EXIT', Pid, Reason}
并调用 F(Reason)。
我不明白你为什么说spawn_monitor,它没有用在你的情况下。无论如何,如果您在 on_exit 函数中将 link(Pid) 替换为 monitor(process,Pid) ,您甚至不需要使用 trap_exit 因为 monitor 函数不会崩溃如果 Pid 死了。在所有情况下,它 returns 消息 {'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).