web-dev-qa-db-ja.com

Erlangのgen_serverで定期的にアクションを実行する方法は?

さらに、毎分1つのアクションを実行するgen_serverを起動したいと思います。

それをスケジュールするための最良の方法は何ですか?

34
Piotr Usewicz

timer:send_interval/2またはerlang:send_after/3を使用する2つの簡単な選択肢があります。 send_intervalはセットアップが簡単ですが、send_after(Erlangモジュールで使用する場合)は組み込み関数であるため信頼性が高くなります。 効率ガイド を参照してください。

send_afterを使用すると、gen_serverプロセスが過負荷にならないようにすることもできます。 send_interval関数を使用している場合、プロセスが追いつくことができるかどうかに関係なく、メッセージが表示されます。 send_afterhandle_infoに戻る直前に呼び出されると、前のメッセージを処理した後でのみ新しいメッセージをスケジュールできます。より正確な時間追跡が必要な場合でも、send_after(または0)よりも低い時間に動的に設定して?INTERVALをスケジュールして追いつくことができます。

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の代わりに、{trigger, Count}など、必要に応じて状態を含むものを送信できます。

55
Adam Lindberg

タイマーを正確に制御するには、erlang:start_timerを使用して、作成した各タイマー参照を保存することをお勧めします。

erlang:start_timererlang:send_afterとわずかな違いがあります。 http://www.erlang.org/doc/man/erlang.html#start_timer- および ( http://www.erlang.org/doc/man/erlang.html#send_after-

ユースケースの例:

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,
    ...
5
l04m33

実際には、gen_server内に同じことを実現するための組み込みメカニズムがあります。 gen_serverのinit、handle_call、handle_cast、またはhandle_infoメソッドの応答タプルの3番目の要素が整数の場合、その期間が経過すると、ミリ秒単位でtimeoutメッセージがプロセスに送信されます。 ..handle_infoを使用して処理する必要があります。例:

 init(Args)-> 
 ...%最初のタイマーを開始します
 {ok、SomeState、20000}。 %% 20000はタイムアウト間隔です
 
 handle_call(Input、From、State)-> 
 ...%何かをする
 ...%何か他のことをする
 {返信、SomeState、20000}。 %% 20000はタイムアウト間隔です
 
 handle_cast(Input、State)-> 
 ...%何かをする
 ...%何か他のことをする
 {noreply、SomeState、20000}。 %% 20000はタイムアウト間隔です
 
 
 %%タイムアウトメッセージがgen_serverに送信され、handle_infoで処理されます%% 
 handle_info(timeout、State) -> 
 ...%アクションを実行します
 ...%新しいタイマーを開始します
 {noreply、SomeState、20000}。 %%「タイムアウト」は20000ミリ秒後に再送信できます
 
1
arun_suresh

使用できるtimerモジュールもあります。

http://erldocs.com/R14B02/stdlib/timer.html?i=8&search=timer#cancel_timer/1

0
ZeissS