How to run Erlang process periodically with exact time (i.e. 10 ms)

I want to start the erlang batch process every 10 ms (depending on the time of the wall clock), 10 ms should be as accurate as possible; What should be the correct way to implement it?

+5
source share
2 answers

If you need a truly reliable and accurate batch process, you must rely on the actual time of the wall clock using erlang:monotonic_time/0,1 . If you use the Stratus3D method, you will eventually lag behind.

 start_link(Period) when Period > 0, is_integer(Period) -> gen_server:start_link({local, ?SERVER}, ?MODULE, Period, []). ... init(Period) -> StartT = erlang:monotonic_time(millisecond), self() ! tick, {ok, {StartT, Period}}. ... handle_info(tick, {StartT, Period} = S) -> Next = Period - (erlang:monotonic_time(millisecond)-StartT) rem Period, _Timer = erlang:send_after(Next, self(), tick), do_task(), {ok, S}. 

You can check in the shell:

 spawn(fun() -> P = 1000, StartT = erlang:monotonic_time(millisecond), self() ! tick, (fun F() -> receive tick -> Next = P - (erlang:monotonic_time(millisecond)-StartT) rem P, erlang:send_after(Next, self(), tick), io:format("X~n", []), F() end end)() end). 
+6
source

If you really want to be as accurate as possible, and you are sure that your task will take less time than the interval that you want to complete, you can have one lengthy process, rather than run the process every 10 ms. Erlang can spawn a new process every 10 ms, but if there is no reason why you cannot reuse the same process, it is usually not worth the overhead (although this is very small).

I would do something like this in OTP gen_server :

 -module(periodic_task). ... module exports start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). ... Rest of API and other OTP callbacks init([]) -> Timer = erlang:send_after(0, self(), check), {ok, Timer}. handle_info(check, OldTimer) -> erlang:cancel_timer(OldTimer), Timer = erlang:send_after(10, self(), check), do_task(), % A function that executes your task {noreply, Timer}. 

Then run gen_server as follows:

 periodic_task:start_link(). 

As long as gen_server is working (if it works the same as the parent process, since they are connected), the do_task/0 function will execute almost every 10 milliseconds. Please note that this will not be entirely accurate. There will be a drift at run time. The actual interval will be 10 ms + the time required to receive the timer message, cancel the old timer and start a new one.

If you want to start a separate process every 10 ms, you can run the do_task/0 process. Please note that this will add extra overhead, but will not necessarily make the spawning interval less accurate.

My example was taken from this answer: What is the best way to do something periodically in Erlang?

0
source

Source: https://habr.com/ru/post/1262024/


All Articles