何时在Erlang中使用超时和监视器?

时间:2014-01-02 03:13:52

标签: erlang

了解你的一些Erlang书籍如下code。为什么这本书有时只使用超时,有时同时使用监视器和超时?在下面是什么需要监视器,因为超时将有效地检测进程是否已关闭?

%% Synchronous call
order_cat(Pid, Name, Color, Description) ->
    Ref = erlang:monitor(process, Pid),
    Pid ! {self(), Ref, {order, Name, Color, Description}},
    receive
        {Ref, Cat} ->
            erlang:demonitor(Ref, [flush]),
            Cat;
        {'DOWN', Ref, process, Pid, Reason} ->
            erlang:error(Reason)
    after 5000 ->
        erlang:error(timeout)
    end.

同时比较以下add_event不使用监视器但subscribe执行

的情况
subscribe(Pid) ->
    Ref = erlang:monitor(process, whereis(?MODULE)),
    ?MODULE ! {self(), Ref, {subscribe, Pid}},
    receive
        {Ref, ok} ->
            {ok, Ref};
        {'DOWN', Ref, process, _Pid, Reason} ->
            {error, Reason}
    after 5000 ->
        {error, timeout}
    end.

add_event(Name, Description, TimeOut) ->
    Ref = make_ref(),
    ?MODULE ! {self(), Ref, {add, Name, Description, TimeOut}},
    receive
        {Ref, Msg} -> Msg
    after 5000 ->
        {error, timeout}
    end.

2 个答案:

答案 0 :(得分:4)

这两个例子之间存在很大差异。

order_cat(Pid, Name, Color, Description)在一个请求的持续时间内使用监视器,并在收到成功响应后调用erlang:demonitor/2

subscribe(Pid)更长久地建立了一个监视器。目的是事件客户端将在其主{'DOWN', Ref, process, Pid, Reason}块中接收receive消息并处理事件服务器死亡的事实。请注意subscribe(Pid)如何将监视器引用返回为{ok, Ref},以供客户端用于此目的。不幸的是,这本书没有显示事件客户端的样子。

现在关于监视器与超时的更普遍的问题:监视的缺点是额外的成本很少,而且复杂性很小。与超时相比的优势包括:

  • 如果目标进程不存在或崩溃尝试处理刚刚发送的消息,则会立即发现,而不是在超时后发现。对于某些应用程序,节省的时间非常重要。
  • 如果目标进程崩溃,监视器消息的Reason部分会告诉您有关原因的信息。在某些应用程序中,客户端可能会根据原因尝试不同的恢复操作。
  • 发送过程知道将来是否期望来自目标进程的响应。在超时的情况下,如果目标进程响应缓慢,则发送进程将在其邮箱中获得响应。如果发件人无法从其邮箱中清除此类响应,则它将变慢并最终终止。这本书涉及Selective Receive section中的这个问题。 gen_server:call/3 documentation也简要解释了这个问题。

gen_server的实现:call / 2使用监视器。由于这是发送生成的erlang请求的数量,因此您可以相信它已经过优化并且是推荐的默认值。事实上,你应该使用OTP而不是自己动手。

请在此处查看source code。这是相关功能:

do_call(Process, Label, Request, Timeout) ->
    try erlang:monitor(process, Process) of
    Mref ->
        %% If the monitor/2 call failed to set up a connection to a
        %% remote node, we don't want the '!' operator to attempt
        %% to set up the connection again. (If the monitor/2 call
        %% failed due to an expired timeout, '!' too would probably
        %% have to wait for the timeout to expire.) Therefore,
        %% use erlang:send/3 with the 'noconnect' option so that it
        %% will fail immediately if there is no connection to the
        %% remote node.

        catch erlang:send(Process, {Label, {self(), Mref}, Request},
          [noconnect]),
        receive
        {Mref, Reply} ->
            erlang:demonitor(Mref, [flush]),
            {ok, Reply};
        {'DOWN', Mref, _, _, noconnection} ->
            Node = get_node(Process),
            exit({nodedown, Node});
        {'DOWN', Mref, _, _, Reason} ->
            exit(Reason)
        after Timeout ->
            erlang:demonitor(Mref, [flush]),
            exit(timeout)
        end
    catch
    error:_ ->
        %% Node (C/Java?) is not supporting the monitor.
        %% The other possible case -- this node is not distributed
        %% -- should have been handled earlier.
        %% Do the best possible with monitor_node/2.
        %% This code may hang indefinitely if the Process 
        %% does not exist. It is only used for featureweak remote nodes.
        Node = get_node(Process),
        monitor_node(Node, true),
        receive
        {nodedown, Node} -> 
            monitor_node(Node, false),
            exit({nodedown, Node})
        after 0 -> 
            Tag = make_ref(),
            Process ! {Label, {self(), Tag}, Request},
            wait_resp(Node, Tag, Timeout)
        end
    end.

答案 1 :(得分:2)

  

为什么这本书有时只使用超时,有时同时使用监视器和超时?

该过程可能会在超时到期之前终止,但您不希望无缘无故地等待更长时间。您希望呼叫最多 五秒,而不是总是五秒钟。

  

在下面是什么需要监视器,因为超时将有效地检测进程是否已关闭?

不会;终止不会触发超时,时间确实如此。