iT邦幫忙

DAY 15
1

蠻可愛的 Erlang 與 Elixir系列 第 15

Concurrent的receive with timeout以及註冊process

昨天的範例中使用了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.


上一篇
Concurrent的client server 方式以及產生process所需時間
下一篇
Concurrent程式設計之Processes錯誤狀況處理
系列文
蠻可愛的 Erlang 與 Elixir30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言