Periodic message sending
A post about erlang, but applies to all other languages.
Let us want to do something once a second, for example, to check for the presence of a file via HTTP url. The process for this we started, it is necessary that he would do something regularly enough.
There are three functions that interest us in erlang: timer: send_interval, timer: send_after, and erlang: send_after.
First, I will explain why send_interval cannot be used.
timer: send_interval is problematic in that it sends messages without checking if the previous one has been processed. As a result, if the task starts to stick, then our process only deals with this task. In a bad case, the accumulation of messages in the queue begins, a memory leak and a complete loss of responsiveness of the process.
I have repeatedly observed several hundred check messages in the message_queue of the process.
In addition, the rape of an external resource begins: a hard disk or a remote server. Indeed, instead of taking a break per second, it may not be at all.
So, the right step is to forward the message to yourself at the end of the task processing.
Between timer: send_after and erlang: send_after, the choice is obvious: erlang: send_after. The timer module does this quite non-optimally and many timers start to create problems.
The only reason not to use erlang: send_after is when there are many thousands of processes and you can group messages without loading the system with timers that are difficult to process, but this is an extremely rare situation.
However, it is easy to make a mistake here:
What happens if someone else sends a check message to the process? It will generate a second wave, because each check message leads to a guaranteed re-timer.
As a result, if you send 20 messages, then the process at each moment in time will have 20 timers, the number of which will not decrease.
The correct answer is:
The explicit removal of the timer leads to the fact that if you send 1000 messages, they will all be processed and after that the process will quickly normalize and there will be only one wave of message forwarding.
Let us want to do something once a second, for example, to check for the presence of a file via HTTP url. The process for this we started, it is necessary that he would do something regularly enough.
There are three functions that interest us in erlang: timer: send_interval, timer: send_after, and erlang: send_after.
First, I will explain why send_interval cannot be used.
timer: send_interval is problematic in that it sends messages without checking if the previous one has been processed. As a result, if the task starts to stick, then our process only deals with this task. In a bad case, the accumulation of messages in the queue begins, a memory leak and a complete loss of responsiveness of the process.
I have repeatedly observed several hundred check messages in the message_queue of the process.
In addition, the rape of an external resource begins: a hard disk or a remote server. Indeed, instead of taking a break per second, it may not be at all.
So, the right step is to forward the message to yourself at the end of the task processing.
Between timer: send_after and erlang: send_after, the choice is obvious: erlang: send_after. The timer module does this quite non-optimally and many timers start to create problems.
The only reason not to use erlang: send_after is when there are many thousands of processes and you can group messages without loading the system with timers that are difficult to process, but this is an extremely rare situation.
However, it is easy to make a mistake here:
init([]) ->
self() ! check,
{ok, state}.
handle_info(check, State) ->
do_job(State),
erlang:send_after(1000, self(), check),
{noreply, State}.
What happens if someone else sends a check message to the process? It will generate a second wave, because each check message leads to a guaranteed re-timer.
As a result, if you send 20 messages, then the process at each moment in time will have 20 timers, the number of which will not decrease.
The correct answer is:
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}.
The explicit removal of the timer leads to the fact that if you send 1000 messages, they will all be processed and after that the process will quickly normalize and there will be only one wave of message forwarding.