Post about Erlang, but applicable to all other languages.
Let us want to do something once a second, for example, to check the availability of a file via HTTP URL. The process for this we started, it is necessary that he would do something fairly regularly.
In erlang, there are three functions of interest: 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 is processed. As a result, if the task begins to stick, then our process is only engaged in this task. In the bad case, the accumulation of messages in the queue begins, a memory leak and a complete loss of responsiveness of the process.
I repeatedly observed several hundred check messages in the message_queue process.
In addition, the rape of an external resource begins: a hard disk or a remote server. After all, instead of what to do a break in the second, it may not be at all.
So, the right step is to resend a message to yourself at the end of processing the task.
Between timer: send_after and erlang: send_after the choice is obvious: erlang: send_after. The timer module does this quite sub-optimally and many timers begin to cause problems.
The only reason not to use erlang: send_after is when there are many thousands of processes and you can group the distribution of messages without loading the system with timers that are not easy 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 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 time point 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 only one wave of message re-sending will remain.