如何使用Erlang的gen_server定期执行操作?

Pio*_*icz 34 erlang gen-server

我想启动一个gen_server,另外,每分钟都会执行一个动作.

安排这个的最佳方法是什么?

Ada*_*erg 55

你有两个简单的选择,使用timer:send_interval/2erlang:send_after/3.send_interval更容易设置,而send_after(当在Erlang模块中使用时)更可靠,因为它是内置函数,请参阅效率指南.

使用send_after还可确保gen_server进程不会过载.如果您正在使用该send_interval功能,无论该过程是否能够跟上,您都会得到一条消息.通过send_after在返回之前调用,只有在handle_info处理完前一个消息后才会安排新消息.如果您想要更准确的时间跟踪,您仍然可以send_after将动态时间设置为低于?INTERVAL(或甚至0)的时间来跟上.

我会在你的建议中推荐以下内容gen_server:

-define(INTERVAL, 60000). % One minute

init(Args) ->
   ... % Start first timer
   erlang:send_after(?INTERVAL, self(), trigger),
   ...

handle_info(trigger, State) ->
   ... % Do the action
   ... % Start new timer
   erlang:send_after(?INTERVAL, self(), trigger),
   ...
Run Code Online (Sandbox Code Playgroud)

trigger如果需要,可以发送带状态的东西,而不是像{trigger, Count}某种东西.


l04*_*m33 5

为了精确地控制计时器,您可能需要使用erlang:start_timer,并保存您创建的每个计时器引用。

erlang:start_timer与稍有不同erlang:send_after,请参阅http://www.erlang.org/doc/man/erlang.html#start_timer-3http://www.erlang.org/doc/man/erlang.html#send_after-3

用例示例:

init(Args) ->
    ...
    TRef = erlang:start_timer(?INTERVAL, self(), trigger),
    State = #state{tref = TRef},
    ...

handle_info({timeout, _Ref, trigger}, State) ->
    %% With this cancel call we are able to manually send the 'trigger' message 
    %% to re-align the timer, and prevent accidentally setting duplicate timers
    erlang:cancel(State#state.tref),
    ...
    TRef = erlang:start_timer(?INTERVAL, self(), trigger),
    NewState = State#state{tref = TRef},
    ...

handle_cast(stop_timer, State) ->
    TRef = State#state.tref,
    erlang:cancel(TRef),

    %% Remove the timeout message that may have been put in our queue just before 
    %% the call to erlang:cancel, so that no timeout message would ever get 
    %% handled after the 'stop_timer' message
    receive
        {timeout, TRef, _} -> void
        after 0 -> void
    end,
    ...
Run Code Online (Sandbox Code Playgroud)