Erlang:带监视器的生成过程
Erlang: spawning process with monitor
我正在研究 Joe Armstrong 的 Programming Erlang 2nd E。本书每章末都有习题。第 13 章,练习 1 说:
Write a function my_spawn(Mod, Func, Args)
that behaves like spawn(Mod, Func, Args)
but with one difference. If the spawned process dies, a message should be printed saying why the process died and how long the process lived for before it died.
这是一个存在竞争条件的解决方案:
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).
但是这个returns的PID是监控进程的,不是Func
进程的。鉴于 spawn
总是 return 它创建的进程的 PID,似乎没有办法在不诉诸副作用的情况下 return Pid
。
实现原子生成的惯用方法是什么?
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
您可以将来自监控进程的 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.
我正在研究 Joe Armstrong 的 Programming Erlang 2nd E。本书每章末都有习题。第 13 章,练习 1 说:
Write a function
my_spawn(Mod, Func, Args)
that behaves likespawn(Mod, Func, Args)
but with one difference. If the spawned process dies, a message should be printed saying why the process died and how long the process lived for before it died.
这是一个存在竞争条件的解决方案:
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).
但是这个returns的PID是监控进程的,不是Func
进程的。鉴于 spawn
总是 return 它创建的进程的 PID,似乎没有办法在不诉诸副作用的情况下 return Pid
。
实现原子生成的惯用方法是什么?
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
您可以将来自监控进程的 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.