简体   繁体   English

Erlang:带监视器的产卵过程

[英]Erlang: spawning process with monitor

I'm working through Joe Armstrong's Programming Erlang 2nd E .我正在研究 Joe Armstrong 的Programming Erlang 2nd E The book has exercises at the end of each chapter.本书每章末尾附有习题。 Chapter 13, exercise 1 says:第 13 章,练习 1 说:

Write a function my_spawn(Mod, Func, Args) that behaves like spawn(Mod, Func, Args) but with one difference.编写一个函数my_spawn(Mod, Func, Args) ,它的行为类似于spawn(Mod, Func, Args)但有一个区别。 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.如果生成的进程死了,应该打印一条消息,说明该进程死的原因以及该进程在死之前存活了多长时间。

Here's a solution that has a race condition:这是一个具有竞争条件的解决方案:

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.

Spawning the process and creating the monitor is not an atomic step, so if the process dies after spawning but before the monitor is created, we won't get the error message.生成进程并创建监视器不是原子步骤,因此如果进程在生成后但在创建监视器之前终止,我们将不会收到错误消息。

Here's an attempt without the race condition:这是没有竞争条件的尝试:

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).

But the PID this returns is that of the monitoring process, not the Func process.但是这个返回的PID是监控进程的PID,而不是Func进程的PID。 And given that spawn always returns the PID of the process it creates, there doesn't seem to be a way to return Pid without resorting to a side effect.鉴于spawn总是返回它创建的进程的 PID,似乎没有办法在不诉诸副作用的情况下返回Pid

What's the idiomatic way to do implement the atomic spawning?实现原子生成的惯用方法是什么?

You can send Pid from monitoring process as a message:您可以从监控进程发送 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.

Another option is to wrap the function in a fun with an init phase :另一种选择是将函数包装在一个有趣的 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 http://marcelog.github.io/articles/erlang_link_vs_monitor_difference.html

The difference between spawn_link and spaw_monitor is well explained here. 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