关于erlang中的timer:send_after

本文深入探讨了Erlang中的`timer:send_after/3`函数,对比了它与`erlang:send_after/3`的区别。通过源码分析,指出`timer:send_after/3`利用ETS表进行定时任务存储,并借助gen_server的超时机制。文章总结了四个关键点,包括定时器存储、超时机制、时间精度以及推荐使用`erlang:send_after/3`的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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函数吧


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值