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

来源:互联网 发布:淘宝花呗分期怎么用 编辑:程序博客网 时间:2024/05/16 11:00


方法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 afteropcode实现,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.

0 0
原创粉丝点击