gpt4 book ai didi

erlang - 是erlang :send_after/3 and timer:send_after/3 intended to behave differently?

转载 作者:行者123 更新时间:2023-12-01 21:25:08 26 4
gpt4 key购买 nike

我想在延迟后向进程发送消息,并发现了 erlang:send_after/4

当查看docs时看起来这正是我想要的:

erlang:send_after(Time, Dest, Msg, Options) -> TimerRef

Starts a timer. When the timer expires, the message Msg is sent to the process identified by 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>}}

并且,docs对于 timer:send_after/3 状态与 erlang 版本几乎相同:

send_after(Time, Pid, Message) -> {ok, TRef} | {error, Reason}

Evaluates Pid ! Message after Time milliseconds.

所以问题是,为什么这两个函数表面上做同样的事情,但行为却不同? erlang:send_after 是否损坏或广告错误?或者也许 timer:send_after 没有按照我的想法进行?

最佳答案

TL;DR

您的假设是正确的:它们的目的是做同样的事情,但实现方式不同。

讨论

timer中的事物模块如 timer:send_after/2,3通过将其定义为服务的 gen_server 来工作。与任何其他服务一样,如果您为其分配大量任务(要跟踪的计时器),该服务可能会过载。

erlang:send_after/3,4另一方面,是直接在运行时内实现的 BIF,因此可以访问硬件定时器等系统原语。如果您有大量计时器,这绝对是您的最佳选择。不过,在大多数程序中您不会注意到其中的差异。

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.

解决方法

在没有相同节点限制的情况下获得 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.

上面的例子有点肤浅,但希望它表达了要点。应该可以得到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-to-Erlang 级别上的 Erlang 消息。

也许我们两者都有是件好事,但是这绝对应该在文档中明确标记,但显然不是(我刚刚检查过)。

关于erlang - 是erlang :send_after/3 and timer:send_after/3 intended to behave differently?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46800163/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com