2009-04-20 19 views
28

Tôi có một quy trình cần thực hiện một số công việc sau mỗi mười lăm giây. Tôi hiện đang làm việc đó như thế này:Cách tốt nhất để làm điều gì đó định kỳ trong Erlang?

 
    -behavior(gen_server). 

    interval_milliseconds()-> 15000. 
    init()-> 
     {ok, 
     _State = FascinatingStateData, 
     _TimeoutInterval = interval_milliseconds() 
     }. 

    %% This gets called automatically as a result of our handlers 
    %% including the optional _TimeoutInterval value in the returned 
    %% Result 
    handle_info(timeout, StateData)-> 
     {noreply, 
     _State = do_some_work(StateData), 
      _TimeoutInterval = interval_milliseconds() 
     }. 

này hoạt động, nhưng nó rất giòn: nếu tôi muốn dạy máy chủ của tôi một tin nhắn mới, khi tôi viết bất kỳ chức năng xử lý mới, tôi phải nhớ để bao gồm các khoảng thời gian chờ tùy chọn trong giá trị trả về của nó. Nghĩa là, nói nếu tôi xử lý một cuộc gọi đồng bộ, tôi cần phải làm điều này:

 
    %% Someone wants to know our state; tell them 
    handle_call(query_state_data, _From, StateData)-> 
     {reply, StateData, _NewStateData = whatever(), interval_milliseconds()}; 

thay vì

 
    %% Someone wants to know our state; tell them 
    handle_call(query_state_data, _From, StateData)-> 
     {reply, StateData, _NewStateData = whatever()}; 

Như bạn có thể đoán, tôi đã thực hiện điều đó rất nhầm lẫn một số lần. Thật khó chịu, bởi vì một khi mã xử lý thông điệp query_state_data đó, thời gian chờ không còn được tạo ra nữa, và toàn bộ máy chủ bị ngừng lại. (Tôi có thể "khử rung" bằng tay bằng cách lấy một vỏ trên máy và gửi một thông báo "hết giờ" bằng tay, nhưng ... eww.)

Bây giờ, tôi có thể cố nhớ luôn chỉ định tham số Timeout tùy chọn trong giá trị kết quả của tôi. Nhưng điều đó không quy mô: Tôi sẽ quên một ngày nào đó, và sẽ nhìn chằm chằm vào lỗi này một lần nữa. Vì vậy: một cách tốt hơn là gì?

Tôi không nghĩ rằng tôi muốn viết một vòng lặp thực sự chạy mãi mãi, và dành phần lớn thời gian của mình ngủ; dường như phản đối tinh thần của OTP.

Trả lời

19

Cách tốt nhất là:

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

Đã một vài năm kể từ khi tôi chạm vào Erlang, nhưng điều này trông giống như những gì tôi đã làm. Cảm ơn. – offby1

7

Sử dụng các module timer :)

+0

Nó không phải là giải pháp tốt nhất: http://erlang.org/doc/efficiency_guide/commoncaveats.html#id60206 – mspanc

35

Sử dụng timer: send_interval/2. Ví dụ:

-behavior(gen_server). 

interval_milliseconds()-> 15000. 
init()-> 
    timer:send_interval(interval_milliseconds(), interval), 
    {ok, FascinatingStateData}. 

%% this clause will be called every 15 seconds 
handle_info(interval, StateData)-> 
    State2 = do_some_work(StateData) 
    {noreply, State2}. 
+1

/me smacks trán Cảm ơn :) – offby1

+1

trừ khi bạn cần thời gian chờ chính xác phụ mili giây và sau đó bạn cần phải cuộn giải pháp của riêng bạn –

+19

Tôi đã chọn sử dụng erlang: send_after, chứ không phải là timer: send_interval và tôi nghĩ nó sẽ được chiếu sáng để giải thích lý do. Đó là bởi vì handle_info của tôi có thể mất nhiều thời gian để hoàn thành rằng nó vẫn đang chạy khi khoảng thời gian tiếp theo đến, và tôi không muốn các thông báo hết thời gian chồng lên nhau trong hàng đợi. Bằng cách sử dụng erlang: send_after (một lần trong hàm init, và một lần nữa ở cuối hàm handle_info (timeout, ...)), tôi có thể đảm bảo rằng mỗi lần hết thời gian chờ đến _at ít nhất_ interval_milliseconds sau lần trước đó. Điều này có thể không phù hợp với mọi người, nhưng có vẻ như phù hợp với tôi. – offby1

Các vấn đề liên quan