在Erlang中定期做某事的最佳方法是什么?

问题描述 投票:32回答:3

我有一个过程,需要每十五秒做一些工作。我目前正在这样做:

-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消息,就不再产生超时,整个服务器陷入停顿。 (我可以通过在计算机上安装外壳并手动发送“超时”消息来手动“除颤”,但是... eww。)

现在,我可以记住始终在Result值中指定该可选的Timeout参数。但这并不能扩展:有一天我会忘记的,并且将再次盯着这个错误。所以:有什么更好的方法?

我不认为我想编写一个永久运行的循环,并且将大部分时间都花在睡眠上;这似乎与OTP的精神背道而驰。

erlang otp
3个回答
24
投票

最好的方法是:

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}.

40
投票

使用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}.

7
投票

使用timer模块:)

© www.soinside.com 2019 - 2024. All rights reserved.