我有一个过程需要每十五秒做一些工作.我现在这样做:
-behavior(gen_server). interval_milliseconds ()-> 15000. init()-> {ok, _State = FascinatingStateData, _TimeoutInterval = interval_milliseconds () }. %% This gets called automatically as a result of our handlers %% including the optional _TimeoutInterval value in the returned %% Result handle_info(timeout, StateData)-> {noreply, _State = do_some_work(StateData), _TimeoutInterval = interval_milliseconds () }.
这是有效的,但它非常脆弱:如果我想教我的服务器一条新消息,当我编写任何新的处理函数时,我必须记住在其返回值中包含可选的超时间隔.也就是说,如果我正在处理同步调用,我需要这样做:
%% Someone wants to know our state; tell them handle_call(query_state_data, _From, StateData)-> {reply, StateData, _NewStateData = whatever (), interval_milliseconds ()};
代替
%% Someone wants to know our state; tell them handle_call(query_state_data, _From, StateData)-> {reply, StateData, _NewStateData = whatever ()};
正如你可能猜到的那样,我多次犯了这个错误.这很讨厌,因为一旦代码处理了query_state_data消息,就不再生成超时,并且整个服务器都会停止运行.(我可以通过在机器上获取一个shell并手动发送"超时"消息来手动"除颤"它,但是...... eww.)
现在,我可以尝试记住始终在Result值中指定可选的Timeout参数.但这不会扩展:我有一天会忘记,并会再次盯着这个错误.那么:什么是更好的方式?
我不认为我想写一个永远运行的实际循环,并且大部分时间都在睡觉; 这似乎与OTP的精神背道而驰.
使用timer:send_interval/2.例如:
-behavior(gen_server). interval_milliseconds()-> 15000. init()-> timer:send_interval(interval_milliseconds(), interval), {ok, FascinatingStateData}. %% this clause will be called every 15 seconds handle_info(interval, StateData)-> State2 = do_some_work(StateData) {noreply, State2}.
最好的方法是:
init([]) -> Timer = erlang:send_after(1, self(), check), {ok, Timer}. handle_info(check, OldTimer) -> erlang:cancel_timer(OldTimer), do_task(), Timer = erlang:send_after(1000, self(), check), {noreply, Timer}.
使用计时器模块:)