《Learn You Some Erlang》一书有以下代码。为什么本书有时只使用超时,有时同时使用监视器和超时?在下面,由于超时将有效地检测进程是否已关闭,因此监视器需要什么?
%% 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.
Run Code Online (Sandbox Code Playgroud)
另请比较以下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.
Run Code Online (Sandbox Code Playgroud)
这两个例子之间有很大的区别。
order_cat(Pid, Name, Color, Description)在一个请求期间使用监视器,并erlang:demonitor/2在收到成功响应后调用。
subscribe(Pid)建立更永久的监视器。目的是事件客户端将{'DOWN', Ref, process, Pid, Reason}在其主receive块中接收消息并处理事件服务器死亡的事实。请注意如何subscribe(Pid)返回监视器引用以{ok, Ref}供客户端用于此目的。不幸的是,这本书没有展示事件客户端的样子。
现在关于监视器与超时的更普遍的问题:监视的缺点是少量的额外成本和少量的复杂性。与超时相比,优点包括:
Reason监视器消息的一部分会告诉您原因。在某些应用中,客户端可能会根据原因尝试不同的恢复操作。gen_server:call/3文档还简要解释了这个问题。gen_server:call/2 的实现使用监视器。由于这是发送的生产 erlang 请求数,因此您可以相信它经过了很好的优化,并且是推荐的默认值。事实上,您应该使用 OTP,而不是自己动手。
请参阅此处的源代码。这是相关的功能:
do_call(进程、标签、请求、超时) ->
尝试 erlang:monitor(process, Process) 的
参考文献->
%% 如果monitor/2调用未能建立到a的连接
%% 远程节点,我们不需要 '!' 操作员尝试
%% 重新建立连接。(如果监视器/2调用
%% 由于超时而失败,'!' 也可能会
%%必须等待超时到期。)因此,
%% 使用 erlang:send/3 和 'noconnect' 选项,这样它
如果没有连接到%%将立即失败
%% 远程节点。
catch erlang:send(Process, {Label, {self(), Mref}, Request},
[无连接]),
收到
{Mref,回复} ->
erlang:demonitor(Mref, [flush]),
{好的,回复};
{'DOWN', Mref, _, _, noconnection} ->
节点 = get_node(进程),
退出({nodedown,节点});
{'向下', Mref, _, _, 原因} ->
退出(原因)
超时后->
erlang:demonitor(Mref, [flush]),
退出(超时)
结尾
抓住
错误:_ ->
%% 节点(C/Java?)不支持监视器。
%% 另一种可能的情况——该节点不是分布式的
%%——应该早点处理。
%% 使用monitor_node/2 做到最好。
%% 如果进程
%% 不存在。它仅用于功能较弱的远程节点。
节点 = get_node(进程),
Monitor_node(节点,真),
收到
{nodedown, 节点} ->
Monitor_node(节点,假),
退出({nodedown,节点})
0 之后 ->
标签 = make_ref(),
过程 !{标签,{self(),标签},请求},
wait_resp(节点、标签、超时)
结尾
结尾。