What's the best way to do something periodically in Erlang?

怎甘沉沦 提交于 2019-11-27 00:14:22

问题


I have a process that needs to do some work every fifteen seconds. I'm currently doing it like this:

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

This works, but it's extremely brittle: if I want to teach my server a new message, when I write any new handler function, I have to remember to include the optional timeout interval in its return value. That is, say if I'm handling a synchronous call, I need to do this:

    %% Someone wants to know our state; tell them
    handle_call(query_state_data, _From, StateData)->
        {reply, StateData, _NewStateData = whatever (), interval_milliseconds ()};

instead of

    %% Someone wants to know our state; tell them
    handle_call(query_state_data, _From, StateData)->
        {reply, StateData, _NewStateData = whatever ()};

As you might guess, I've made that very mistake a number of times. It's nasty, because once the code handles that query_state_data message, the timeouts no longer get generated, and the whole server grinds to a halt. (I can "defibrillate" it manually by getting a shell on the machine and sending a "timeout" message by hand, but ... eww.)

Now, I could try to remember to always specify that optional Timeout parameter in my Result value. But that doesn't scale: I'll forget someday, and will be staring at this bug once again. So: what's a better way?

I don't think I want to write an actual loop that runs forever, and spends most of its time sleeping; that seems counter to the spirit of OTP.


回答1:


The best way is:

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



回答2:


Use timer:send_interval/2. E.g.:

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



回答3:


Use the timer module :)



来源:https://stackoverflow.com/questions/769466/whats-the-best-way-to-do-something-periodically-in-erlang

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!