erlang中的process——程序是輕量級的,并且程序間無共享。查了很多資料,似乎沒人說清楚輕量級程序算是什麼概念,繼續查找中。。。閑話不提,進入并發程式設計的世界。本文算是學習筆記,也可以說是《concurrent programming in erlang》第五張的簡略翻譯。
1.程序的建立
程序是一種自包含的、分隔的計算單元,并與其他程序并發運作在系統中,在程序間并沒有一個繼承體系,當然,應用開發者可以設計這樣一個繼承體系。
程序的建立使用如下文法:
pid = spawn(module, functionname, argumentlist)
spawn接受三個參數:子產品名,函數名以及參數清單,并傳回一個代表建立的程序的辨別符(pid)。
如果在一個已知程序pid1中執行:
pid2 = spawn(mod, func, args)
那麼,pid2僅僅能被pid1可見,erlang系統的安全性就建構在限制程序擴充的基礎上。
2.程序間通信
erlang程序間的通信隻能通過發送消息來實作,消息的發送使用!符号:
pid ! message
其中pid是接受消息的程序标記符,message就是消息。接受方和消息可以是任何的有效的erlang結構,隻要他們的結果傳回的是程序标記符和消息。
消息的接受是使用receive關鍵字,文法如下:
receive
message1 [when guard1] ->
actions1 ;
message2 [when guard2] ->
actions2 ;
end
每一個erlang程序都有一個“郵箱”,所有發送到程序的消息都按照到達的順序存儲在“郵箱”裡,上面所示的消息message1,message2,當它們與“郵箱”裡的消息比對,并且限制(guard)通過,那麼相應的actionn将執行,并且receive傳回的是actionn的最後一條執行語句的結果。erlang對“郵箱”裡的消息比對是有選擇性的,隻有比對的消息将被觸發相應的action,而沒有比對的消息将仍然保留在“郵箱”裡。這一機制保證了沒有消息會阻塞其他消息的到達。
消息到達的順序并不決定消息的優先級,程序将輪流檢查“郵箱”裡的消息進行嘗試比對。消息的優先級别下文再講。
如何接受特定程序的消息呢?答案很簡單,将發送方(sender)也附送在消息當中,接收方通過模式比對決定是否接受,比如:
pid ! {self(),abc}
給程序pid發送消息{self(),abc},利用self過程得到發送方作為消息發送。然後接收方:
{pid1,msg} ->
通過模式比對決定隻有pid1程序發送的消息才接受。
3.一些例子
僅說明下書中計數的程序例子,我添加了簡單注釋:
-module(counter).
-compile(export_all).
% start(),傳回一個新程序,程序執行函數loop
start()->spawn(counter, loop,[0]).
% 調用此操作遞增計數
increment(counter)->
counter!increament.
% 傳回目前計數值
value(counter)->
counter!{self(),value},
receive
{counter,value}->
%傳回給調用方
value
end.
%停止計數
stop(counter)->
counter!{self(),stop}.
loop(val)->
receive
%接受不同的消息,決定傳回結果
increament->
loop(val+1);
{from,value}->
from!{self(),val},
loop(val);
stop->
true;
%不是以上3種消息,就繼續等待
other->
loop(val)
end.
調用方式:
1> counter1=counter:start().
<0.30.0>
2> counter:value(counter1).
3> counter:increment(counter1).
increament
4> counter:value(counter1).
1
基于程序的消息傳遞機制可以很容易地實作有限狀态機(fsm),狀态使用函數表示,而事件就是消息。具體不再展開
4.逾時設定
erlang中的receive文法可以添加一個額外選項:timeout,類似:
message1 [when guard1] ->
actions1 ;
message2 [when guard2] ->
actions2 ;
after
timeoutexpr ->
actionst
after之後的timeoutexpr表達式傳回一個整數time(毫秒級别),時間的精确程度依賴于erlang在作業系統或者硬體的實作。如果在time毫秒内,沒有一個消息被選中,逾時設定将生效,也就是actiont将執行。time有兩個特殊值:
1)infinity(無窮大),infinity是一個atom,指定了逾時設定将永遠不會被執行。
2) 0,逾時如果設定為0意味着逾時設定将立刻執行,但是系統将首先嘗試目前“郵箱”裡的消息。
逾時的常見幾個應用,比如挂起目前程序多少毫秒:
sleep(time) ->
receive
after time ->
true
end.
比如清空程序的“郵箱”,丢棄“郵箱”裡的所有消息:
flush_buffer() ->
anymessage ->
flush_buffer()
after 0 ->
将目前程序永遠挂起:
suspend() ->
after
infinity ->
true
end.
逾時也可以應用于實作定時器,比如下面這個例子,建立一個程序,這個程序将在設定時間後向自己發送消息:
-module(timer).
-export([timeout/2,cancel/1,timer/3]).
timeout(time, alarm) ->
spawn(timer, timer, [self(),time,alarm]).
cancel(timer) ->
timer ! {self(),cancel}.
timer(pid, time, alarm) ->
receive
{pid,cancel} ->
true
after time ->
pid ! alarm
5、注冊程序
為了給程序發送消息,我們需要知道程序的pid,但是在某些情況下:在一個很大系統裡面有很多的全局servers,或者為了安全考慮需要隐藏程序pid。為了達到可以發送消息給一個不知道pid的程序的目的,我們提供了注冊程序的辦法,給程序們注冊名字,這些名字必須是atom。
基本的調用形式:
register(name, pid)
将name與程序pid聯系起來
unregister(name)
取消name與相應程序的對應關系。
whereis(name)
傳回name所關聯的程序的pid,如果沒有程序與之關聯,就傳回atom:undefined
registered()
傳回目前注冊的程序的名字清單
6.程序的優先級
設定程序的優先級可以使用bifs:
process_flag(priority, pri)
pri可以是normal、low,預設都是normal
優先級高的程序将相對低的執行多一點。
7.程序組(process group)
所有的erlang程序都有一個pid與一個他們共有的稱為group leader相關聯,當一個新的程序被建立的時候将被加入同一個程序組。最初的系統程序的group leader就是它自身,是以它也是所有被建立程序及子程序的group leader。這就意味着erlang的程序被組織為一棵tree,其中的根節點就是第一個被建立的程序。下面的bifs被用于操縱程序組:
group_leader()
傳回執行程序的group leader的pid
group_leader(leader, pid)
設定程序pid的group leader為程序的leader
8.erlang的程序模型很容易去建構client-server的模型,書中有一節專門讨論了這一點,着重強調了接口的設計以及抽象層次的隔離問題,不翻譯了