我正在通过Joe Armstrong的Programming Erlang 2nd E工作。这本书在每章末尾都有练习。第13章,练习1说:
编写一个函数
my_spawn(Mod, Func, Args)
,其行为与spawn(Mod, Func, Args)
相似,但有一点不同。如果生成的进程死亡,则应打印一条消息,说明进程死亡的原因以及进程在其死亡之前的生存时间。
这是一个具有竞争条件的解决方案:
my_spawn(Mod, Func, Args) ->
Pid = spawn(Mod, Func, Args),
spawn(fun() ->
Ref = monitor(process, Pid),
T1 = erlang:monotonic_time(millisecond),
receive
{'DOWN', Ref, process, Pid, Why} ->
io:format("~p died because of ~p~n", [Pid, Why]),
io:format("~p lived for ~p ms~n", [Pid, erlang:monotonic_time(millisecond) - T1])
end
end),
Pid.
生成进程并创建监视器不是原子步骤,因此如果进程在生成后但在创建监视器之前死亡,我们将不会收到错误消息。
这是一次没有竞争条件的尝试:
my_spawn_atomic(Mod, Func, Args) ->
spawn(fun() ->
{Pid, Ref} = spawn_monitor(Mod, Func, Args),
T1 = erlang:monotonic_time(millisecond),
receive {'DOWN', Ref, process, Pid, Why} ->
io:format("~p died because of ~p~n", [Pid, Why]),
io:format("~p lived for ~p ms~n", [Pid, erlang:monotonic_time(millisecond) - T1])
end
end).
但是返回的PID是监控过程,而不是Func
过程。鉴于spawn
总是返回它创建的进程的PID,似乎没有办法在不诉诸副作用的情况下返回Pid
。
实施原子生成的惯用方法是什么?
答案 0 :(得分:1)
您可以将监视过程中的Pid作为消息发送:
my_spawn_atomic(Mod, Func, Args) ->
Parent = self(),
MPid = spawn(fun() ->
{Pid, Ref} = spawn_monitor(Mod, Func, Args),
Parent ! {spawned, self(), Pid},
T1 = erlang:monotonic_time(millisecond),
receive {'DOWN', Ref, process, Pid, Why} ->
io:format("~p died because of ~p~n", [Pid, Why]),
io:format("~p lived for ~p ms~n", [Pid, erlang:monotonic_time(millisecond) - T1])
end
end),
receive
{spawned, MPid, Pid} -> Pid
after 1000 -> error % 1s should be way enough for spawning monitoring process
end.
另一个选择是使用初始阶段
将函数包装好my_spawn(Mod, Func, Args) ->
Pid = spawn(fun() ->
receive run -> apply(Mod, Func, Args)
after 1000 -> exit(init_timeout)
end
end),
spawn(fun() ->
Ref = monitor(process, Pid),
T1 = erlang:monotonic_time(millisecond),
Pid ! run,
receive
{'DOWN', Ref, process, Pid, Why} ->
io:format("~p died because of ~p~n", [Pid, Why]),
io:format("~p lived for ~p ms~n", [Pid, erlang:monotonic_time(millisecond) - T1])
end
end),
Pid.
答案 1 :(得分:0)
http://marcelog.github.io/articles/erlang_link_vs_monitor_difference.html
这里很好地解释了spawn_link和spaw_monitor之间的区别。
-module(mon_test).
-export([my_spawn/3, die_in/1]).
my_spawn(Mod, Func, Args) ->
spawn(my_spawn(mon_test, my_spawn, [self(), Mod, Func, Args]),
receive
Pid -> Pid
after 1000 -> timeout
end.
my_spawn(Parent, Mod, Func, Args) ->
{Pid, Ref} = spawn_monitor(Mod, Func, Args),
T1 = erlang:system_time(),
Parent ! Pid,
receive
{'DOWN', Ref, _Any, Pid, Why} ->
io:format("~p died because of ~p, lived for ~p milliseconds~n", [Pid, Why, (erlang:system_time()-T1)/1000/1000])
end.
die_in(Secs) ->
receive
Reason -> exit(Reason)
after Secs*1000 -> exit(timeout_reason)
end.
> mon_test:my_spawn(mon_test, die_in, [5]).
<0.155.0>
<0.155.0> died because of timeout_reason, lived for 5001.152 milliseconds