繁体   English   中英

Erlang:带监视器的产卵过程

[英]Erlang: spawning process with monitor

我正在研究 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是监控进程的PID,而不是Func进程的PID。 鉴于spawn总是返回它创建的进程的 PID,似乎没有办法在不诉诸副作用的情况下返回Pid

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

您可以从监控进程发送 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.

另一种选择是将函数包装在一个有趣的 init 阶段

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.

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM