erlang gen-server

¿Cómo realizar acciones periódicamente con gen_server de Erlang?



gen-server (4)

Quiero iniciar gen_server que, además, realizará una acción cada minuto.

¿Cuál es la mejor manera de programar eso?


En realidad, hay un mecanismo incorporado en gen_server para lograr lo mismo. Si el tercer elemento de la tupla de respuesta de los métodos init, handle_call, handle_cast o handle_info en el gen_server es un número entero, se enviará un mensaje de timeout al proceso después de ese período de tiempo en milisegundos ... que debería manejarse usando handle_info. Por ejemplo:

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


Para controlar con precisión el temporizador, puede usar erlang:start_timer y guardar cada referencia de temporizador que haya creado.

erlang:start_timer tiene una pequeña diferencia con erlang:send_after , ver http://www.erlang.org/doc/man/erlang.html#start_timer-3 y http://www.erlang.org/doc/man/erlang.html#send_after-3

Ejemplo de caso de uso:

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



Tienes dos alternativas fáciles, usa el timer:send_interval/2 o erlang:send_after/3 . send_interval es más fácil de configurar, mientras que send_after (cuando se usa en el módulo Erlang) es más confiable ya que es una función incorporada, consulte la Guía de Eficiencia .

Usar send_after también asegura que el proceso gen_server no esté sobrecargado. Si estuviera utilizando la función send_interval , recibiría un mensaje independientemente de si el proceso puede mantenerse o no. Con send_after ser llamado justo antes de la devolución en handle_info , solo planificas un nuevo mensaje una vez que manejaste el anterior. Si desea un seguimiento del tiempo más preciso, puede programar un send_after con el tiempo configurado dinámicamente a algo inferior a ?INTERVAL (o incluso 0) para ponerse al día.

Yo recomendaría algo a lo largo de las siguientes líneas en su 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), ...

En lugar de trigger , puede enviar algo con un estado si es necesario, como {trigger, Count} o algo así.