方法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的一個模板:你可以點擊這里看源碼
它隨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處理。
問題:
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.