Erlang服務器代碼的分析與學習(一)

The Echo process

%%------------------------------------------------------------

-module(echo).
-compile(export_all).

go() ->
register(echo, spawn(echo, loop, [])),
echo ! {self(), hello},
Pid = whereis(echo),
receive
{Pid, Msg} -> 
io:format("~w~n", [Msg])
end,
echo ! stop.


loop() ->
receive
{From, Msg} ->
From ! {self(), Msg},
loop();
stop ->
true
end.

 %%------------------------------------------------------------

代碼易錯點:

(1)io:format/2函數的第二個參數是個list,務必記得寫成[]的形式。

(2)receive expression內部匹配模式以分號分隔,內部以逗號分隔,end之前的模式結尾處不加分號。


流程:

(1)母進程執行go()。

(2)spawn函數創建一個子進程來執行echo模塊中的loop函數,函數參數列表是[],將創建的子進程的Pid作爲參數傳入register函數的第二個參數,將這個子進程起個別名叫做echo。

(子線程被創建完成後,就開始執行Echo:loop())

(3)子線程執行loop(),進入receive狀態,等待接收消息,此時mailbox爲空,所以子線程suspend。

(4)母線程繼續執行,向ID爲Pid的線程(即子線程)發送消息,內容是個tuple,第一個元素是self()。返回的是母線程自己的Pid,第二個元素是個atom,hello。

(這裏whereis(Alias)的用處是返回別名Alias對應的線程的Pid,因爲erlang編譯器的原因,receive expression的匹配模式內{Whereis(echo), Msg}這樣的嵌套格式會返回"illegal pattern"的錯誤,更不能將此處的Pid用Alias代替,因爲Alias是個atom,匹配模式內此處應當是個Pid,是<0.101.0>這樣的格式。)

(5)接着,母線程在receive語句下進入suspend狀態,等待接收消息。

(6)子線程接收到母線程發來的消息{self(), hello},成功匹配了{From, Msg},這裏From與self()即母線程Pid匹配,Msg與atom類型的變量hello匹配。

(7)匹配成功後,子線程用接收到的母線程的Pid(即匹配後的From)向母線程發送消息,消息內容也是個tuple,第一個元素是self(),返回的是子線程自己的Pid,第二個元素是Msg(即匹配後,母線程之前發來的內容,hello)。

(8)發送完成後,子線程通過自我調用loop(),重新執行receive語句進入suspend狀態等待消息。

(9)剛剛母線程在receive語句下進入suspend狀態,現在收到了子線程發來的反饋消息,於是將收到的消息與{Pid, Msg}進行模式匹配,Pid爲之前母線程中創建子線程的Pid,與子線程消息中傳來的self()匹配成功,Msg與發來的hello匹配(綁定)。並作爲參數傳入下面調用的io:format/2函數,在shell裏面將它打印出來。

(10)打印完成後,母線程向子線程發送stop,子線程收到stop,與第二行的stop模式匹配成功,返回true,通過一層層向上返回,最後end。子線程中的loop()函數執行結束。


%% -------------------------------------------------------------------------------------------------------------------------------------------------


必備函數介紹:

spawn(Module, Function, Arguments)

爲了運行併發性的代碼,我們需要創建更多的線程,我們使用spawn(Module, Function, Arguments)這個BIF(built-in function)來建一個新的線程,它會執行從模塊Module中exported出來的函數Function,Function的參數列表是Arguments。這個spawn/3函數會返回新建線程的Pid(Process Identifier)。


register(Alias, Pid)

一般我們用別名來註冊那些提供特定服務的線程,這些別名可以被當成那些線程的Pid使用。註冊一個線程的BIF是register(Alias, Pid),這裏的Alias是個atom,Pid是被註冊的線程的ID。


Pid ! Message

這表示將Message(可以是List,Tuple等等)發送到進程標識爲Pid的進程的消息隊列中。即當前進程向Pid的進程發送Message。


receive / end

進程使用receive關鍵字,從process mailbox中獲取消息。receive語句是一個由分隔的receive和end關鍵字組成的結構,中間可以包含多個語句。一旦執行receive語句,process mailbox裏面第一條消息(最舊的)會輪流與receive表達式裏面的每一個pattern進行匹配。

receive關鍵詞被用來讓進程等待從其他進程發來的消息。

 • If a successful match  occurs,the message is retrieved from the mailbox, the variables in the pattern arebound to the matching parts of the message, and the body of the clause isexecuted.

• If none of the clauses matches , thesubsequent messages in the mailbox are patternmatched one by one against all ofthe clauses until either a message matches a clause or all of the messages havefailed all of the possible pattern matches.


%% -------------------------------------------------------------------------------------------------------------------------------------------------


以下部分來源於網頁內容(原文網址:http://www.erlang-cn.com/121.htmlerlang 2013年11月03日 於 Erlang教程 發表

“Erlang 進程之間的消息可以是任何簡單的 Erlang 項。比如說,可以是列表、元組、整數、原子、進程標識等等。 

每個進程都有獨立的消息接收隊列。新接收的消息被放置在接收隊列的尾部。當進程執行receive 時,消息中第一個消息與與 receive 後的第一個模塊進行匹配。如果匹配成功,則將該消息從消息隊列中刪除,並執行該模式後面的代碼。 

然而,如果第一個模式匹配失敗,則測試第二個匹配。如果第二個匹配成功,則將該消息從消息隊列中刪除,並執行第二個匹配後的代碼。如果第二個匹配也失敗,則匹配第三個,依次類推,直到所有模式都匹配結束。如果所有匹配都失敗,則將第一個消息留在消息隊列中,使用第二個消息重複前面的過程。第二個消息匹配成功時,則執行匹配成功後的程序並將消息從消息隊列中取出(將第一個消息與其餘的消息繼續留在消息隊列中)。如果第二個消息也匹配失敗,則嘗試第三個消息,依次類推,直到嘗試完消息隊列所有的消息爲止。如果所有消息都處理結束(匹配失敗或者匹配成功被移除),則進程阻塞,等待新的消息的到來。上面的過程將會一直重複下去。 

Erlang 實現是非常 “聰明” 的,它會盡量減少 receive 的每個消息與模式匹配測試的次數。”

 




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章