如何使用精确时间(即10ms)定期运行Erlang进程

时间:2016-12-29 11:10:36

标签: timer erlang

我想每10ms运行一次定期的erlang进程(基于挂钟时间),10ms应该尽可能准确;应该以什么方式实施它?

2 个答案:

答案 0 :(得分:6)

如果您想要真正可靠和准确的定期处理,则应使用erlang:monotonic_time/0,1依赖实际的挂钟时间。如果您使用Stratus3D' answer中的方法,您最终会落后。

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(),
    {noreply, S}.

你可以在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).

答案 1 :(得分:0)

如果你真的想要尽可能精确,并且你确定你的任务花费的时间少于你希望它执行的时间间隔,那么你可以有一个长时间运行的过程而不是每10ms生成一个进程。 Erlang可以每10ms生成一个新进程,但除非有理由不能重复使用相同的进程,否则它通常不值得花费(即使它很少)。

我会在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}.

然后像这样启动gen_server:

periodic_task:start_link().

只要gen_server正在运行(如果父进程因为链接而崩溃,那么函数do_task/0将几乎每10毫秒执行一次。请注意,这不是完全准确的。执行时间会有所不同。实际间隔为10ms +接收定时器消息所需的时间,取消旧定时器,然后启动新定时器。

如果你想每隔10毫秒启动一个单独的进程,你可以让do_task/0产生一个进程。请注意,这会增加额外的开销,但不一定会使生成之间的间隔不准确。

我的例子来自这个答案:What's the best way to do something periodically in Erlang?