我曾经用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.