2011-05-04 5 views

Respuesta

54

Tienes dos alternativas fáciles, utiliza timer:send_interval/2 o erlang:send_after/3. send_interval es más fácil de instalar, mientras que send_after (cuando se utiliza en el módulo de Erlang) es más fiable ya que es una función incorporada, ver el Efficiency Guide.

El uso de 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 del regreso de handle_info sólo se programe un nuevo mensaje una vez que manejaste la anterior. Si quieres que el tiempo de seguimiento más preciso todavía se puede programar una send_after con el tiempo ajustado dinámicamente a algo más bajo que ?INTERVAL (o incluso 0) para ponerse al día.

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 usted podría enviar algo con un estado si es necesario, al igual que {trigger, Count} o algo así.

+1

Eso es genial! Es exactamente lo que estoy haciendo en este momento. ¡Gracias! –

+0

¿No se va a desincronizar eventualmente, porque el temporizador dispara cada 6000 + ms? –

+0

@PatrickOscity Claro, podría. Si quiere estar realmente seguro, puede calcular un horario futuro para programarlo y calcular la cantidad exacta de milisegundos para enviar después. –

1

No es en realidad un mecanismo incorporado dentro gen_server a 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 timeout al proceso después de ese período de tiempo en milisegundos ... que debería manejarse usando handle_info . Para, 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 

+0

¿por qué me dieron un voto de -1? –

+0

Eso es verdad. Aunque significa que debe manipular el tiempo de espera, lo que podría no ser una mala idea. –

+10

'timeout' no está destinado a la ejecución periódica. Se pretende iniciar alguna acción o finalizar cuando no ocurre nada en este período. Este tiempo de espera finaliza con cada acción incluso con algunas acciones del sistema ('sys',' proc_lib', ...). En breve, se desaconseja utilizar el tiempo de espera, excepto algunas cosas de "mantenimiento", como la terminación automática o las limpiezas. –

3

Para controlar con precisión el temporizador, es posible que desee utilizar erlang:start_timer, y guardar cada referencia temporizador que ha 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, 
    ... 
Cuestiones relacionadas