[Erlang 0046] Erlang Timer-阿里云开发者社区

开发者社区> 微服务> 正文

[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 的测试

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.

测试结果:

同样起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的进程给另一个进程发送消息的流程是这样的:消息放到对端的队列 同时如果对端不是可运行的,加入运行队列。 调度器最终调度对端, 对端取出消息,进行处理。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
微服务
使用钉钉扫一扫加入圈子
+ 订阅

构建可靠、高效、易扩展的技术基石

其他文章