我想启动一个 gen_server ,它每分钟执行一个动作。
安排它的最佳方式是什么?
您有两个简单的选择,使用timer:send_interval/2
或erlang: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),
...
trigger
如果需要,您可以发送带有状态的东西,而不是像{trigger, Count}
或其他东西。
要精确控制计时器,您可能需要使用erlang:start_timer
,并保存您创建的每个计时器引用。
erlang:start_timer
与 有细微差别erlang:send_after
,请参阅http://www.erlang.org/doc/man/erlang.html#start_timer-3和http://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,
...
实际上在 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 毫秒后再次发送