2016-12-29 78 views

回答

6

如果你想真正可靠和准确的周期性过程中,你应该使用erlang:monotonic_time/0,1依靠实际的挂钟时间。如果您使用Stratus3Danswer中的方法,您最终会落后。

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}. 

可以在壳体试验:

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). 
+0

使用这种方法的好处是,小不可避免的错误不会在时间积累。 – Pascal

0

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

我会做这样的事情在OTP gen_server:作为gen_server运行

periodic_task:start_link(). 

只要(如果它崩溃所以才会父:

-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过程,因为它们被链接),功能do_task/0几乎每10毫秒执行一次。请注意,这不会完全准确。执行时间会有一个漂移。实际的时间间隔将是10ms +接收定时器消息所需的时间,取消旧定时器并启动新定时器。

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

我的例子,从这个答案采取:What's the best way to do something periodically in Erlang?