37

我想启动一个 gen_server ,它每分钟执行一个动作。

安排它的最佳方式是什么?

4

4 回答 4

59

您有两个简单的选择,使用timer:send_interval/2erlang:send_after/3send_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),
   ...

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

于 2011-05-04T13:22:10.827 回答
7

要精确控制计时器,您可能需要使用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,
    ...
于 2012-09-13T06:27:13.147 回答
1

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

初始化(参数)->
   ... % 启动第一个计时器
   {好的,SomeState,20000}。%% 20000 是超时间隔

处理调用(输入,从,状态)->
   ... % 做点什么
   ... % 做其他事情
   {回复,SomeState,20000}。%% 20000 是超时间隔

handle_cast(输入,状态)->
   ... % 做点什么
   ... % 做其他事情
   {noreply,SomeState,20000}。%% 20000 是超时间隔


%% 超时消息发送到 gen_server 以在 handle_info 中处理 %%
处理信息(超时,状态)->
   ... % 采取行动
   ... % 开始新的计时器
   {noreply,SomeState,20000}。%% "timeout" 可以在 20000 毫秒后再次发送

于 2011-05-05T03:30:49.860 回答
0

还有timer可以使用的模块。

http://erldocs.com/R14B02/stdlib/timer.html?i=8&search=timer#cancel_timer/1

于 2011-05-06T06:53:32.370 回答