我想在延迟后向进程发送消息,但发现了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})
对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>}}
而且,timer:send_after/3
的文档几乎与erlang
版本的文档相同:send_after(Time, Pid, Message) -> {ok, TRef} | {error, Reason}
在时间毫秒后评估Pid!消息。
所以问题是,为什么这两个函数表面上做同样的事情,行为却不同?erlang:send_after
坏了吗?或者是错误的宣传?或者timer:send_after
没有做我认为它应该做的事情?
2条答案
按热度按时间unguejic1#
TL;DR
Your assumption is correct: these are intended to do the same thing, but are implemented differently.
Discussion
Things in the
timer
module such astimer:send_after/2,3
work through the gen_server that defines that as a service. Like any other service, this one can get overloaded if you assign a really huge number of tasks (timers to track) to it.erlang:send_after/3,4
, on the other hand, is a BIF implemented directly within the runtime and therefore have access to system primitives like the hardware timer. If you have a ton of timers this is definitely the way to go. In most programs you won't notice the difference, though.There is actually a note about this in the Erlang Efficiency Guide:
3.1 Timer Module
Creating timers using erlang:send_after/3 and erlang:start_timer/3 , is much more efficient than using the timers provided by the timer module in STDLIB. The timer module uses a separate process to manage the timers. That process can easily become overloaded if many processes create and cancel timers frequently (especially when using the SMP emulator).
The functions in the timer module that do not manage timers (such as timer:tc/3 or timer:sleep/1), do not call the timer-server process and are therefore harmless.
A workaround
A workaround to gain the efficiency of the BIF without the same-node restriction is to have a process of your own that does nothing but wait for a message to forward to another node:
The above example is a bit shallow, but hopefully it expresses the point. It should be possible to get the efficiency of the BIF
erlang:send_after/3,4
but still manage to send messages across nodes as well as give you the freedom to cancel a message usingerlang:cancel_timer/1
But why?
The puzzle (and bug) is why
erlang:send_after/3,4
does not want to work across nodes. The example you provided above looks a bit odd as the first assignment toP
was the Pid<10101.10.0>
, but the crashed call was reported as<10585.83.0>
-- clearly not the same.For the moment I do not know why
erlang:send_after/3,4
doesn't work, but I can say with confidence that the mechanism of operation between the two is not the same. I'll look into it, but I imagine that the BIF version is actually doing some funny business within the runtime to gain efficiency and as a result signalling the target process by directly updating its mailbox instead of actually sending an Erlang message on the higher Erlang-to-Erlang level.Maybe it is good that we have both, but this should definitely be clearly marked in the docs, and it evidently is not (I just checked).
6g8kf2rb2#
如果你有很多定时器,超时顺序会有一些不同。下面的例子显示 erlang:send_after 不保证顺序,但是 timer:send_after 保证顺序。