我想在延迟之后向进程发送消息并发现erlang:send_after/4.
在查看文档时,它看起来就像我想要的那样:
erlang:send_after(Time, Dest, Msg, Options) -> TimerRef启动计时器.当计时器到期时,消息Msg被发送到由Dest标识的进程.
但是,当目标在另一个节点上运行时它似乎不起作用 - 它告诉我其中一个参数是坏的.
1> P = spawn('node@host', module, function, [Arg]).
<10585.83.0>
2> erlang:send_after(1000, P, {123}).
** exception error: bad argument
in function erlang:send_after/3
called as erlang:send_after(1000,<10585.83.0>,{123})
Run Code Online (Sandbox Code Playgroud)
做同样的事timer:send_after/3似乎工作正常:
1> P = spawn('node@host', module, function, [Arg]).
<10101.10.0>
2> timer:send_after(1000, P, {123}).
{ok,{-576458842589535,#Ref<0.1843049418.1937244161.31646>}}
Run Code Online (Sandbox Code Playgroud)
而且,文档的timer:send_after/3状态几乎是同样的事情erlang版本:
send_after(Time, Pid, Message) -> {ok, TRef} | {error, Reason}评估Pid!时间毫秒后的消息.
所以问题是,为什么这两个函数在表面上做同样的事情时表现不同?被erlang:send_after打破,或错误广告?或者可能timer:send_after没有做我认为的事情?
您的假设是正确的:它们旨在做相同的事情,但实现方式不同。
事情的timer模块,例如timer:send_after/2,3通过gen_server的工作,定义,作为一个服务。与其他任何服务一样,如果您为其分配大量任务(要跟踪的计时器),则该服务可能会超载。
erlang:send_after/3,4另一方面,是作为NIF实现的BIF。如果您有大量的计时器,那么这绝对是可行的方法。但是,在大多数程序中,您不会注意到它们之间的差异。
实际上,《Erlang效率指南》中对此有一个注释:
3.1定时器模块
使用erlang:send_after / 3和erlang:start_timer / 3创建计时器比使用STDLIB中的计时器模块提供的计时器效率更高。计时器模块使用单独的进程来管理计时器。如果许多进程频繁创建和取消计时器(特别是在使用SMP仿真器时),则该进程很容易变得过载。
计时器模块中不管理计时器的功能(例如timer:tc / 3或timer:sleep / 1),不会调用timer-server进程,因此是无害的。
一种在没有相同节点限制的情况下提高BIF效率的解决方法是让您自己执行一个过程,该过程除了等待消息转发到另一个节点之外什么都不做:
-module(foo_forward).
-export([send_after/3, cancel/1]).
% Obviously this is an example only. You would want to write this to
% be compliant with proc_lib, write a proper init/N and integrate with
% OTP. Note that this snippet is missing the OTP service functions.
start() ->
spawn(fun() -> loop(self(), [], none) end).
send_after(Time, Dest, Message) ->
erlang:send_after(Time, self(), {forward, Dest, Message}).
loop(Parent, Debug, State) ->
receive
{forward, Dest, Message} ->
Dest ! Message,
loop(Parent, Debug, State);
{system, From, Request} ->
sys:handle_msg(Request, From, Parent, ?MODULE, Debug, State);
Unexpected ->
ok = log(warning, "Received message: ~tp", [Unexpected]),
loop(Parent, Debug, State)
end.
Run Code Online (Sandbox Code Playgroud)
上面的例子有点浅,但是希望它表达了这一点。应该有可能获得BIF的效率,erlang:send_after/3,4但仍然设法跨节点发送消息,并给您自由使用以下方式取消消息的权限erlang:cancel_timer/1
困惑(和错误)就是为什么erlang:send_after/3,4不想跨节点工作。上面提供的示例看起来有点奇怪,因为第一个分配P是Pid <10101.10.0>,但是该崩溃的呼叫被报告为<10585.83.0>-显然不一样。
目前,我不知道为什么 erlang:send_after/3,4不起作用,但是我可以自信地说,两者之间的运作机制并不相同。我会仔细研究一下,但我想BIF版本实际上在运行时中做了一些有趣的事情,以提高效率,并因此通过直接更新其邮箱而不是在较高的Erlang上实际发送Erlang消息来向目标进程发出信号。到Erlang级别。
也许我们两个都好,但是在文档中应该明确标出,显然不是(我刚刚检查过)。
| 归档时间: |
|
| 查看次数: |
770 次 |
| 最近记录: |