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

时间:2011-05-04 12:55:40

标签: erlang gen-server

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

安排该计划的最佳方式是什么?

4 个答案:

答案 0 :(得分:54)

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

使用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),
   ...

如果需要,您可以发送带状态的内容,而不是trigger,而不是{trigger, Count}或其他内容。

答案 1 :(得分:4)

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

erlang:start_timererlang: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,
    ...

答案 2 :(得分:1)

gen_server中实际上有一个内置机制来完成同样的事情。 如果gen_server中的init,handle_call,handle_cast或handle_info方法的响应元组的第三个元素是一个整数,那么在该段时间之后将以毫秒为单位向该进程发送timeout消息。应该使用handle_info来处理。例如:

init(Args) ->
   ... % Start first timer
   {ok, SomeState, 20000}. %% 20000 is the timeout interval

handle_call(Input, From, State) ->
   ... % Do something
   ... % Do something else
   {reply, SomeState, 20000}. %% 20000 is the timeout interval

handle_cast(Input, State) ->
   ... % Do something
   ... % Do something else
   {noreply, SomeState, 20000}. %% 20000 is the timeout interval


%% A timeout message is sent to the gen_server to be handled in handle_info %%
handle_info(timeout, State) ->
   ... % Do the action
   ... % Start new timer
   {noreply, SomeState, 20000}. %% "timeout" can be sent again after 20000 ms

答案 3 :(得分:0)