erlang中有两个延时发送消息的函数erlang:send_after/3, timer:send_after/3,有什么区别呢?
看下timer:send_after/3的源码
send_after(Time, Pid, Message) ->
req(apply_after, {Time, {?MODULE, send, [Pid, Message]}})
req(Req, Arg) ->
SysTime = system_time(),
ensure_started(),
gen_server:call(timer_server, {Req, Arg, SysTime}, infinity)
handle_call({apply_after, {Time, Op}, Started}, _From, _Ts)
when is_integer(Time), Time >= 0 ->
BRef = {Started + 1000*Time, make_ref()},
%返回给调用进程的数据,{到期时间,ref}
Timer = {BRef, timeout, Op},
ets:insert(?TIMER_TAB, Timer),
%插入定时ets表
Timeout = timer_timeout(system_time()),
{reply, {ok, BRef}, [], Timeout}; %这里返回值带有Timout参数,如果超时未能收到其它消息,将会触发超时
%%超时处理函数
timer_timeout(SysTime) ->
case ets:first(?TIMER_TAB) of
'$end_of_table' ->
infinity;
{Time, _Ref} when Time > SysTime ->
Timeout = (Time - SysTime + 999) div 1000,
%% Returned timeout must fit in a small int
erlang:min(Timeout, ?MAX_TIMEOUT);
Key ->
case ets:lookup(?TIMER_TAB, Key) of
[{Key, timeout, MFA}] ->
ets:delete(?TIMER_TAB,Key),
do_apply(MFA),
timer_timeout(SysTime);
[{{Time, Ref}, Repeat = {repeat, Interv, To}, MFA}] ->
ets:delete(?TIMER_TAB,Key),
NewTime = Time + Interv,
%% Update the interval entry (last in table)
ets:insert(?INTERVAL_TAB,{{interval,Ref},{NewTime,Ref},To}),
do_apply(MFA),
ets:insert(?TIMER_TAB, {{NewTime, Ref}, Repeat, MFA}),
timer_timeout(SysTime)
end
end.
%%超时后执行相应的操作
%% Help functions
do_apply({M,F,A}) ->
case {M, F, A} of
{?MODULE, send, A} ->
%发送消息
%% If send op. send directly, (faster than spawn)
catch send(A);
{erlang, exit, [Name, Reason]} ->
catch exit(get_pid(Name), Reason);
_ ->
%% else spawn process with the operation
catch spawn(M,F,A)
%开启新进程,执行M:F(A), 由timer:apply_after/4调用
end.
%%gen_server主循环
loop(Parent, Name, State, Mod, hibernate, Debug) ->
proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]);
loop(Parent, Name, State, Mod, Time, Debug) ->
Msg = receive
Input ->
Input
after Time ->
%超时则产生一个timeout消息
timeout
end,
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, false)
总结:
1. timer模块把定时器保存在一个叫?TIMER_TAB的有序ETS表中
?TIMER_TAB = ets:new(?TIMER_TAB, [named_table,ordered_set,protected]),
2.利用了gen_server返回值的超时机制
3.
Timer模块开头有一句话:
The timeouts are not exact, but should be
at least
as long as requested.
意思是此模块的timeout不是非常精确,但至少保证经过了要求的时间
4.erlang:send_after是BIF函数,
它们把计时器附着在进程自己身上。
so尽量使用erlang:send_after函数吧