[Erlang 0046] Erlang Timer


 我曾經用mochiweb暴露出來一個接口供測試的同事通過鏈接修改測試服務器的時間,但是發現Erlang並沒有馬上使用最新的系統時間,甚至頻繁調整系統時間會導致Erlang節點內各種異常,遂放棄這種方案;測試的同事,修改完系統時間之后重啟各Erlang節點.

看看下面+c的說明就明白了:

erl +c
Disable compensation for sudden changes of system time.
Normally, erlang:now/0 will not immediately reflect sudden changes in the system time, in order to keep timers (including receive-after) working. Instead, the time maintained by erlang:now/0 is slowly adjusted towards the new system time. (Slowly means in one percent adjustments; if the time is off by one minute, the time will be adjusted in 100 minutes.)
When the +c option is given, this slow adjustment will not take place. Instead erlang:now/0 will always reflect the current system time. Note that timers are based on erlang:now/0. If the system time jumps, timers then time out at the wrong time.

doc: http://www.erlang.org/doc/man/erl.html 

 

關於TimerRef

erlang:start_timer(Time, Dest, Msg) -> TimerRef
Types:
Time = int()
0 <= Time <= 4294967295
Dest = LocalPid | RegName
LocalPid = pid() (of a process, alive or dead, on the local node)
RegName = atom()
Msg = term()
TimerRef = reference()
Starts a timer which will send the message {timeout, TimerRef, Msg} to Dest after Time milliseconds.
If Dest is an atom, it is supposed to be the name of a registered process. The process referred to by the name is looked
up at the time of delivery. No error is given if the name does not refer to a process.
If Dest is a pid, the timer will be automatically canceled if the process referred to by the pid is not alive, or when the
process exits. This feature was introduced in erts version 5.4.11. Note that timers will not be automatically canceled
when Dest is an atom.
See also erlang:send_after/3, erlang:cancel_timer/1, and erlang:read_timer/1.
Failure: badarg if the arguments does not satisfy the requirements specified above.


erlang:cancel_timer(TimerRef) -> Time | false
Types:
TimerRef = reference()
Time = int()

Cancels a timer, where TimerRef was returned by either erlang:send_after/3 or erlang:start_timer/3. If the timer is
there to be removed, the function returns the time in milliseconds left until the timer would have expired, otherwise
false (which means that TimerRef was never a timer, that it has already been cancelled, or that it has already
delivered its message).
See also erlang:send_after/3, erlang:start_timer/3, and erlang:read_timer/1.
Note: Cancelling a timer does not guarantee that the message has not already been delivered to the message queue.

doc: http://www.erlang.org/doc/man/erlang.html#start_timer-3

 

TimerRef來取消定時器,看一個典型的范例,來自代碼erl5.8.2\lib\stdlib-1.17.2\src\gen_fsm.erl

%% Returns Ref, sends event {timeout,Ref,Msg} after Time 
%% to the (then) current state.
start_timer(Time, Msg) ->
erlang:start_timer(Time, self(), {'$gen_timer', Msg}).

%% Returns Ref, sends Event after Time to the (then) current state.
send_event_after(Time, Event) ->
erlang:start_timer(Time, self(), {'$gen_event', Event}).

%% Returns the remaing time for the timer if Ref referred to
%% an active timer/send_event_after, false otherwise.
cancel_timer(Ref) ->
case erlang:cancel_timer(Ref) of
false ->
receive {timeout, Ref, _} -> 0
after 0 -> false
end;
RemainingTime ->
RemainingTime
end.

  The timer module

Creating timers using  erlang:send_after/3 and  erlang:start_timer/3 is much more efficient than using the timers

provided by the  timer module. The timer module uses a separate process to manage the timers, and that process can easily become overloaded if many processes create and cancel timers frequently (especially when using the SMP emulator).

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.

 

2014-8-7 11:32:50 一則關於receive  after 的測試

test1(Num)->
    List = lists:seq(1,Num),
    [spawn(?MODULE,recev1,[])||_<-List],
    finish.

recev1()->
    receive
        test->stop
    end.

test2(Num)->
    List = lists:seq(1,Num),
    [spawn(?MODULE,recev2,[])||_<-List],
    finish.

recev2()->
    receive
        test->stop
    after 60000->
            recev2()
    end.

測試結果:

同樣起200萬個 test1 cpu 0% 完全掛起狀態
test2 cpu 100% 性能消耗巨大

 

為什么呢?主要是在after原語背后實際上是創建了timer,下面這里有一段詳細的解釋:

   http://erlang.2086793.n4.nabble.com/Erlang-receive-after-performance-strangeness-td2115642.html

Some performance drop can be expected from using 'after' since whenever 
the receive blocks, it has to perform additional work to set up a timer, 
and timers aren't cheap. And assuming a reply is received in time, the 
timer has to be cancelled, and that's also an overhead. 

And your !/receive combo looks like it will always have to block waiting 
for ProcessId to do its thing, so you'll always incur the timer overhead. 

What you're doing here is a blocking RPC. A more Erlangy way of doing 
things is to have multiple pending asynchronous requests, and to have 
some loop that both generates new requests and receives replies to pending 
requests. That way delays in replies don't block other work. 

 

 

 

 

 

鋒爺關於Timer幾篇文章(標題上有鏈接 ):

[1] erlang:send_after和erlang:start_timer的使用解釋  
" 這時候send_after里面存放的是Msg, 那用戶如何知道Msg是對於那個TimerRef的呢? 讀者可能說, 那我可以在消息里面加入TimerRef. 這個主意不錯, 但是問題是在send_after調用返回之前, 你是無法得到TimerRef, 當然也就無從構造這個消息, 那就無法處理這個可能的超時信息, 就會破壞邏輯.
所以erts version 5.4.11 引入了, start_timer來解決這個問題. 它是自動的在超時后, 要發送消息前, 在消息里面添加了{timeout, TimerRef, Msg}, 達到識別的目的. "

[2] erlang的timer和實現機制 
 整個定時器由bump_timer來驅動。bump_timer是在schedule的時候不定期調用的。總之使用timer的時候要小心,因為timer實在scheduler的線程里面調用的,不能做非常耗時的操作,否則會阻塞調度器。

[3] erlang定時器的強度測試 

每個定時器事件的流程是這樣的 進程檢查消息隊列 沒消息 注冊定時器事件 進程換出 定時器超時 進程換入 處理定時器事件。 所以有大量的消息拷貝 進程調度。 erlang的進程給另一個進程發送消息的流程是這樣的:消息放到對端的隊列 同時如果對端不是可運行的,加入運行隊列。 調度器最終調度對端, 對端取出消息,進行處理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM