如何使用Erlang的gen_server定期执行操作?
我想启动一个gen_server,另外,每分钟会执行一个动作。
什么是最好的方式来安排呢?
你有两个简单的select,使用timer:send_interval/2
或erlang:send_after/3
。 send_interval
更容易设置,而send_after
(当在Erlang模块中使用时)更加可靠,因为它是一个内置函数,请参见效率指南 。
使用send_after
还可以确保gen_server
进程不会被重载。 如果使用send_interval
函数,则无论进程是否可以跟上,都会收到消息。 在handle_info
返回之前调用send_after
,只有在处理完前一个消息之后,才会安排一条新消息。 如果你想更准确的时间跟踪,你仍然可以安排一个send_after
dynamic设置的时间低于?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, Count}
或其他。
要精确控制计时器,您可能需要使用erlang:start_timer
,并保存您创build的每个计时器参考。
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中实际上有一个内置的机制来完成同样的事情。 如果gen_server
中的init,handle_call,handle_cast或handle_info方法的响应元组的第三个元素是一个整数,那么timeout
消息将以毫秒为单位发送到进程,应该使用handle_info进行处理。 例如:
init(Args) - > ...%启动第一个定时器 {ok,SomeState,20000}。 %% 20000是超时间隔 handle_call(Input,From,State) - > ... % 做一点事 ...%做别的事情 {reply,SomeState,20000}。 %% 20000是超时间隔 handle_cast(input,状态) - > ... % 做一点事 ...%做别的事情 {noreply,SomeState,20000}。 %% 20000是超时间隔 %%超时消息被发送到gen_server以在handle_info %%中处理 handle_info(timeout,State) - > ...执行操作 ...%启动新的定时器 {noreply,SomeState,20000}。 可以在20000 ms后再次发送%%“timeout”