昨天的範例中使用了receive,但是process有可能一直沒收到message,
所以receive 有對應的 after, 用來設定等待時間,單位為millisecond.
語法如下:
receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
after Time ->
Expressions
end
我們可以用這個功能來作一個簡單的計時器.
-module(e1015a).
-export([start/2, cancel/1]).
timer(Time, Fun) ->
receive
cancel ->
void
after Time ->
Fun()
end.
%%
start(Time, Fun) ->
spawn(fun() -> timer(Time, Fun) end).
%%
cancel(Pid) -> Pid ! cancel.
運作狀況:
1> c(e1015a).
{ok,e1015a}
2> Pid = e1015a:start(4000, fun() -> io:format("timer event~n") end).
<0.40.0>
3>
timer event
3> Pid2 = e1015a:start(40000, fun() -> io:format("timer event~n") end).
<0.42.0>
4>
4> e1015a:cancel(Pid2).
cancel
第2, 設定4秒後,執行函數,函數為顯示timer event. 經過4秒等待後,如預期運作.
第3, 設定40秒後,執行同樣函數.
我們在第4,使用cancel,傳送cancel 原子給process,經過模式比對後,返回.
看到上面的程式,也許會有一個疑問,cancel -> void 這裡的void是保留字嗎??
是否一定要用void呢?
我們來看下面的程式與實做.
-module(e1015s).
-export([start/2, cancel/1]).
timer(Time, Fun) ->
receive
cancel ->
anythingyouwant
after Time ->
Fun()
end.
%%
start(Time, Fun) ->
spawn(fun() -> timer(Time, Fun) end).
%%
cancel(Pid) -> Pid ! cancel.
實做情況:
5> c(e1015s).
{ok,e1015s}
6> Pid3 = e1015s:start(40000, fun() -> io:format("timer event~n") end).
<0.50.0>
7>
7> e1015s:cancel(Pid3).
cancel
那裡只要模式比對符合cancel原子,然後任意一個原子,就可以了.
看了這些spawn出來的process,都要設法獲得其pid,以傳遞message;
實務上這樣有點麻煩,所以就有register(AnAtom, Pid) 這樣的函數,
只要註冊後,系統中就用原子當名稱,可以溝通.這行為跟大家去報戶口類似.
接著來把上面程式改一下,加上register,並且變成週期性執行的.
-module(e1015t).
-export([start/2, stop/0]).
tick(Time, Fun) ->
receive
stop_msg ->
anythingyouwant
after Time ->
Fun(),
tick(Time, Fun)
% call tick again
end.
%%
start(Time, Fun) ->
register(clock, spawn(fun() -> tick(Time, Fun) end)).
%%
stop() -> clock ! stop_msg.
執行情形:
1> c(e1015t).
{ok,e1015t}
2> e1015t:start(10000, fun() -> io:format("TICK ~p~n", [calendar:now_to_local_time(erlang:now())]) end).
true
TICK {{2014,10,15},{21,2,11}}
TICK {{2014,10,15},{21,2,21}}
TICK {{2014,10,15},{21,2,31}}
TICK {{2014,10,15},{21,2,41}}
TICK {{2014,10,15},{21,2,51}}
3> e1015t:stop().
stop_msg
設定為每10秒,timeout機制生效,執行傳入之函數,此函數利用erlang系統內建函數,顯示本地時刻.
另外注意到,我們將stop/0 改為傳送 stop_msg 給註冊之函數.最終是列印出stop_msg.