• [Erlang09]Erlang gen_server实现定时器(interval)的几种方法及各自的优缺点?


    方法1:

    %%gen_server:部分call_back function.
       -define(TIME,1000).
    
       init([]) –>
          erlang:send_after(?TIME,self(),loop_interval_event),
          {ok, #state{}}.
    
       handle_info(loop_interval_event, State) –>
          NewState = do_loop_interval_event(State),
          erlang:send_after(?TIME,self(),loop_interval_event),
          {noreply, NewState}.

    优点:可以加任意多个定时器,且可以保证do_loop_interval_event/1处理完后才触发第二个定时器【想像一个如果处理event 200ms,处理间隔是150ms,那么这个进程还是可以不阻塞消息队列的】.这种方法也是推荐使用的。

    缺点: 如果项目有很多的进程都有定时器,大家都调用系统函数来判定时间,性能消耗会增大【这个=下讲原因】。

    方法2:

    %%在gen_server:call back function 返回值加入一个时间
    
        init([]) –>
         {ok, #state{},?TIME}.
    
       handle_info(timeout,State = #state{count = Count}) –>
        io:format("timeout:~w~n",[Count]),
        {noreply,State#state{count = Count+1},?TIME}.

    原理:利用gen_server: init返回值如果是{ok,State,TimeTemp}时会在TimeTemp后发出一个timeout信息,给handle_info处理,然后handle_info处理后再设置返回值的time,又会循环触发这个timeout事件,完成定循环功能

    缺点:只能使用一个定时事件哦,就是timeout,且会被其它的call back function :handle_call,handle_cast,影响,因为如果他们的返回值也加入这个TIME,也会触发同一个timeout事件….

    其实gen_server可以设置这个timeout事件,主要目的还是为了怕回调函数处理消息太慢,如果太慢了,就执行相同的timeout做相关处理。

    方法3:

    %%使用timer:send_interval/3设置事件间隔
        init([]) –>
          timer:send_interval(?TIME,self(),loop_interval_event),
         {ok, #state}.   
       handle_info(loop_interval_event, State) ->
          NewState = do_loop_inverval_event(State),
          {noreply, NewState};

    send_interval/3

    Evaluates Pid ! Message repeatedly after Time amount of time has elapsed. (Pid can also be an atom of a registered name.) Returns {ok, TRef} or {error, Reason}.

    就是每隔TIME时间就给Pid发一个Msg,(相当于每TIME Pid ! Msg).

    缺点:timer自已本身就是一个gen_server进程,如果在SMP下大量进程要使用这个进程来频繁调度也是很吃力的.

    优点:当然是简单且可以设置多个。


      以上只是小菜,下面来看看erlang 神秘的time及为什么大部分人都视timer模块为毒药?

    erlang使用time有4种方式:

    语法层:receive after opcode实现,timeout立即把进程加入到调度队列 使用非常多,也是最高效的
    BIF:
    erlang: send_after/3
    erlang: send_timer/3

    timout 立即给Dest Pid发送Msg

    使用较多
    gen_server:
    timer模块
    使用gen_server统一管理用一个ets的管理实现 统一管理erts 定时器
    driver:
    int driver_set_timer(ErlDrvPort port, unsigned long time);
    tcp/udp进程需要超时处理,所以有大量的连接的时候这种timer的数量非常大,定时器超时后把port_task加到调度队列 inet_driver大量使用这个api. 这个没用到,不是很懂…

    这上面只有timer模块是用erlang写的,那么,我们来好好研究下这个简单而有趣的timer模块吧。

    timer是一个典型的gen_server模块,非常简单明了,只有500行左右,也可以做为学习写好gen_server的一个模板:你可以点击这里看源码

     timer

    它随kernel application起动,被kernel_safe_sup监控,注册名为timer_server。是一个标准的gen_server模块,我们现在来看看它有趣的地方:

    它的实现主要是依赖于gen_server call back里面如果:init/1,hande_call/3 ,handle_info/2,handle_cast/2 返回值加入TimeOut参数,那么经过TimeOut时间后,会触发一个timeout事件给handle_info处理。

    timer_01

    问题:

    1. 为什么大部分人视timer为毒药呢?他的局限性是什么?

    timer设计的目的就是:统一管理多少时长后发生的事件,是一个manager进程,同时也是一个单进程,这样的短板:如果大量的事件都在这里面时,就会使这个进程负荷太大,出现各种不稳定bug.这就是大部分人不也使用它的原因;

    2. 什么时候可以使用它呢?

    首先,要明白为什么为把这些事件用timer来统一管理,因为如果大量进程自己内部调用erlang: send_after/3,即当用erlang: send_after/3导致的开销大于使用timer的开销时,自然,我们就会想自己设计一个统一的管理进程来取代每个work进程自己单独用erlang: send_after/3发信息处理:即manger进程 每隔一段时间就给work进程发消息来代替erlang: send_after/3. 自己造这个轮子也可以,不过在这时使用timer模块来处理再好不过啦!!!!

    Tip:你可以使用timer:get_status()来查看这个进程的负载情况

    关于timer的误解:

    1. 所有timer模块都是单进程的,使用一定要慎重考虑再考虑!

    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.

    有一些timer内不管理时间定时器的函数 例如:

    sleep/1,
       tc/1, tc/2, tc/3, 
       now_diff/2,
       seconds/1, minutes/1, hours/1, hms/3

    不去调用定时器server进程的函数都是无害的。【也就是说:有些timer里面的函数是不依赖于这个server的,可以随意用】

    2. Warning:

    A timer can always be removed by calling cancel/1.

    An interval timer, i.e. a timer created by evaluating any of the functions apply_interval/4, send_interval/3, and send_interval/2, is linked to the process towards which the timer performs its task.

    A one-shot timer, i.e. a timer created by evaluating any of the functions apply_after/4, send_after/3, send_after/2, exit_after/3, exit_after/2, kill_after/2, and kill_after/1 is not linked to any process. Hence, such a timer is removed only when it reaches its timeout, or if it is explicitly removed by a call to cancel/1.

  • 相关阅读:
    UEditor中多图上传的bug
    Mysql 5.5 数据库命令大全
    Android学习书籍
    众方中继使用说明
    MyEclipse 2015优化技巧
    Android studio1.3.1 快捷键
    你有没有这样的猜疑?
    右键新建没有Word_Excel_PowerPoint的解决方法
    电脑清理桌面快捷图标小箭头
    Spring3.1+Hibernate3+Struts2的最新整合所需要的jar包
  • 原文地址:https://www.cnblogs.com/zhongwencool/p/erlang_timer.html
Copyright © 2020-2023  润新知