# [Erlang 0046] Erlang Timer

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.

# 关于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.
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).
Note: Cancelling a timer does not guarantee that the message has not already been delivered to the message queue.

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.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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.

test2 cpu 100% 性能消耗巨大

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.

[1] erlang:send_after和erlang:start_timer的使用解释
" 这时候send_after里面存放的是Msg, 那用户如何知道Msg是对于那个TimerRef的呢? 读者可能说, 那我可以在消息里面加入TimerRef. 这个主意不错, 但是问题是在send_after调用返回之前, 你是无法得到TimerRef, 当然也就无从构造这个消息, 那就无法处理这个可能的超时信息, 就会破坏逻辑.

[2] erlang的timer和实现机制
整个定时器由bump_timer来驱动。bump_timer是在schedule的时候不定期调用的。总之使用timer的时候要小心，因为timer实在scheduler的线程里面调用的，不能做非常耗时的操作，否则会阻塞调度器。

+ 订阅