2016-12-29 80 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?