【问题标题】:Running statements in shell gives different output在 shell 中运行语句给出不同的输出
【发布时间】:2017-04-09 11:21:59
【问题描述】:

简单代码:

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

在外壳中:

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 以退出原因 Reason 终止,则发送“DOWN”消息 到 Pid1:

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

如果 Pid2 不存在,则立即发送“DOWN”消息 原因设置为 noproc

【问题讨论】:

  • 如果在Pid1 ! hello. 之前添加timer:sleep(100), 会得到预期的输出吗?
  • @Dogbert,是的!这似乎表明spawn() 异步执行。

标签: erlang


【解决方案1】:

您的代码包含竞争条件 -- spawn 是异步的,并且可能在进程生成之前返回,并且您可能最终在 on_exit:on_exit/2 开始监视它之前发送并崩溃 Pid1,这会导致 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

代码在 shell 中运行良好可能是因为 Erlang VM 在 shell 中执行某些操作的速度比编译代码时慢,但不能保证这种行为。这是一个经典的比赛条件。

Erlang 对此有一个解决方案:erlang:spawn_monitor/{1,3}。此函数保证在生成该函数后立即附加监视器。你必须重新安排你的代码来使用它而不是spawn/3 + erlang:monitor/1

【讨论】:

  • 谢谢。我没有费心检查monitor/2 的定义,因为我正在阅读有关进程的erlang 文档——它没有提到它是异步的,这是一个重要的细节。但是,现在我检查了定义:注意!监控请求是一个异步信号。也就是说,信号到达目的地需要时间。 Wtf?!我可以在其中添加执行 monitor/2 的乐趣的事件回调循环在哪里?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-12
  • 2012-11-02
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 1970-01-01
相关资源
最近更新 更多