Я хочу запустить gen_server, который, кроме того, будет выполнять одно действие каждую минуту.
Каков наилучший способ запланировать это?
Я хочу запустить gen_server, который, кроме того, будет выполнять одно действие каждую минуту.
Каков наилучший способ запланировать это?
У вас есть две простые альтернативы, используйте timer:send_interval/2
или erlang:send_after/3
. send_interval
проще настроить, а send_after
(при использовании в модуле Erlang) более надежна, поскольку является встроенной функцией, см. Эффективность Руководство.
Использование send_after
также гарантирует, что процесс gen_server
не будет перегружен. Если вы использовали функцию send_interval
, вы получили бы сообщение независимо от того, будет ли процесс поддерживать или нет. При вызове send_after
непосредственно перед возвратом в handle_info
вы планируете новое сообщение только после того, как вы обработали предыдущий. Если вы хотите более точное отслеживание времени, вы все равно можете запланировать send_after
с динамическим временем, которое будет установлено ниже ?INTERVAL
(или даже 0), чтобы догнать.
Я бы рекомендовал что-то в следующих строках в 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}
или что-то.
Чтобы точно управлять таймером, вы можете использовать erlang:start_timer
и сохранить каждую созданную ссылку на таймер.
erlang:start_timer
имеет небольшую разницу с erlang:send_after
, см. http://www.erlang.org/doc/man/erlang.html#start_timer-3 и http://www.erlang.org/doc/man/erlang.html#send_after-3
Пример использования:
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,
...
В gen_server есть встроенный механизм для выполнения того же самого.
Если третий элемент кортежа ответа методов init, handle_call, handle_cast или handle_info в gen_server
является целым числом, сообщение timeout
будет отправлено в процесс после этого периода времени в миллисекундах..., который должен быть обрабатывается с помощью handle_info. Например,
init(Args) -> ... % Start first timer {ok, SomeState, 20000}. %% 20000 is the timeout interval handle_call(Input, From, State) -> ... % Do something ... % Do something else {reply, SomeState, 20000}. %% 20000 is the timeout interval handle_cast(Input, State) -> ... % Do something ... % Do something else {noreply, SomeState, 20000}. %% 20000 is the timeout interval %% A timeout message is sent to the gen_server to be handled in handle_info %% handle_info(timeout, State) -> ... % Do the action ... % Start new timer {noreply, SomeState, 20000}. %% "timeout" can be sent again after 20000 ms
Существует также модуль timer
, который можно использовать.
http://erldocs.com/R14B02/stdlib/timer.html?i=8&search=timer#cancel_timer/1