2009-04-20 5 views
28

私は15秒ごとにいくつかの作業を行う必要があるプロセスがあります。私は現在、このようにそれをやっている:Erlangで定期的に何かを行う最良の方法は何ですか?

 
    -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メッセージを処理すると、タイムアウトが生成されなくなり、サーバー全体が停止するため、コードは厄介です。 (私はマシン上でシェルを取得し、手で "タイムアウト"メッセージを送ることによって手動で "除細動"することができます...)

これで、オプションのタイムアウトパラメータ私の結果値に。しかし、それは縮尺どおりではありません。私はいつか忘れて、このバグをもう一度見つめていきます。だから、より良い方法は何ですか?

私は永遠に走る実際のループを書いて、睡眠のほとんどを過ごすとは思っていません。それはOTPの精神に反するようです。 send_interval/2:

答えて

19

最良の方法は次のとおりです。

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

Erlangに触れてから数年が経ちましたが、これは私が何をしているかのように見えます。ありがとう。 – offby1

7

timerモジュール:)

+0

それが最善の解決策ではありません。http://erlang.org/doc/efficiency_guide/commoncaveats.html#id60206 – mspanc

35

使用timerを使用してください。例:

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

/私は額 を萌芽ありがとう:) – offby1

+1

正確なタイムアウトがサブミリ秒を必要としない限り、あなた自身のソリューションをロールバックする必要があります –

+19

私は、send_intervalというタイマではなく、erlang:send_afterを使用することにしました。これは、私のhandle_infoが完了するのに時間がかかり、次の間隔が来てもまだ実行されている可能性があり、タイムアウトメッセージをキューに積み重ねたくないからです。 erlang:send_after(init関数で1回、handle_info(timeout、...)関数の最後にもう一度)を使用することで、各タイムアウトが前のタイムアウトよりも少なくとも_ interval_milliseconds後になるようにすることができます。 これはすべての人にとって適切ではないかもしれませんが、それは私にとっては正しいようです。 – offby1

関連する問題