运行报表给出不同的输出

问题描述:

简单代码:运行报表给出不同的输出

-module(on_exit). 
-export([on_exit/2, test/0]). 

on_exit(Pid, Fun) -> 
    spawn(fun() -> 
        Ref = erlang:monitor(process, Pid), 
        receive 
         {'DOWN', Ref, process, Pid, Why} -> 
          Fun(Why) 
        end 
      end). 

test() -> 
    Fun1 = fun() -> receive Msg -> list_to_atom(Msg) end end, 
    Pid1 = spawn(Fun1), 

    Fun2 = fun(Why) -> io:format("~w died with error: ~w~n", [Pid1, Why]) end, 
    _Pid2 = spawn(on_exit, on_exit, [Pid1, Fun2]), 

    Pid1 ! hello. 

在shell:

1> c(on_exit). 
{ok,on_exit} 

2> on_exit:test(). 
<0.39.0> died with error: noproc 
hello 

3> 
=ERROR REPORT==== 9-Apr-2017::05:16:54 === 
Error in process <0.39.0> with exit value: {badarg,[{erlang,list_to_atom,[hello],[]},{on_exit,'-test/0-fun-0-',0,[{file,"on_exit.erl"},{line,14}]}]} 

预期输出:

5> Pid1 ! hello. 
<0.35.0> died with error: {badarg,[{erlang,list_to_atom,[hello],[]}]} 
hello 

6> 
=ERROR REPORT==== 9-Apr-2017::05:15:47 === 
Error in process <0.35.0> with exit value: {badarg,[{erlang,list_to_atom,[hello],[]}]} 

事实上,预期输出是我看看我是否将test()中的每行都粘贴到了shell中。为什么我在函数内运行相同的行时会出现noproc(无进程)错误?

docs

12.8监视器

的替代方法链路监视器。进程Pid1可以通过调用BIF erlang:monitor(process,Pid2)为Pid2创建一个 监视器。 函数返回一个引用Ref。

如果PID2与退出原因原因终止,一个“向下”消息被发送 到PID1:

{'DOWN', Ref, process, Pid2, Reason} 

如果PID2不存在时,“向下”消息被立即用 原因集发送到noproc

+0

如果您在Pid1之前添加'timer:sleep(100)',您会得到预期的输出! hello.'? – Dogbert

+0

@Dogbert,是的!这似乎表明'spawn()'异步执行。 – 7stud

你的代码包含一个竞争条件 - spawn是异步过程之前可能会返回被催生,你可能最终发送和崩溃Pid1on_exit:on_exit/2前开始监控它,这将导致erlang:monitor/2呼吁立即发送noproc消息给来电者:

1> Pid = spawn(fun() -> ok end). 
<0.59.0> 
2> erlang:monitor(process, Pid). 
#Ref<0.0.1.106> 
3> flush(). 
Shell got {'DOWN',#Ref<0.0.1.106>,process,<0.59.0>,noproc} 
ok 

代码工作在外壳精可能是因为二郎VM在shell当代码被编译比慢慢地执行一些事情,但这种行为是不能保证。这是一个经典的竞赛条件。

Erlang为此提供了一个解决方案:erlang:spawn_monitor/{1,3}。保证该功能在产生功能后立即连接显示器。您将不得不重新安排您的代码来使用它,而不是spawn/3 + erlang:monitor/1

+0

谢谢。我没有打扰检查'monitor/2'的定义,因为我正在阅读有关进程的erlang文档 - 并没有提到它是异步的,这是一个重要的细节。但是,现在我检查了定义:*注意! 监视器请求是一个异步信号。也就是说,信号到达目的地需要时间。* Wtf ?!事件回调循环哪里可以添加执行monitor/2的乐趣? – 7stud