Erlang:带监视器的产生过程

时间:2018-02-04 21:36:31

标签: erlang monitoring spawn

我正在通过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

实施原子生成的惯用方法是什么?

2 个答案:

答案 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