2013-09-29 28 views
4

這段代碼是我們的老師給我們的,但遺憾的是沒有解釋。我們在班上試了一下,然後被解僱了。Erlang的併發性及其正確的工作流程

如果有人能夠徹底向我解釋這段代碼,那將會非常有幫助。提前致謝。

-module(pingpong). 
-compile(export_all). 

start_pong() -> 
    register(pong, spawn(pingpong,pong,[])). 

pong() -> 
    receive 
     finished -> 
      io:format("Pong finished ~n"); 
     {ping, Ping_Pid} -> 
      io:format("i am the receiver ~n"), 
     Ping_Pid ! pong, 
     pong() 
end. 

start_ping(Pong_Node) -> 
    spawn(pingpong, ping, [3, Pong_Node]). 

ping(0, Pong_Node) -> 
    {pong, Pong_Node} ! finished, 
    io:format("Pong finished ~n"); 

ping(N, Pong_Node) -> 
    {pong, Pong_Node} ! {ping, self()}, 
    receive 
     pong -> 
      io:format("i am the sender ~n") 
    end, 
    ping(N-1,Pong_Node). 

回答

8

讓我們來看看前兩行。

-module(pingpong). 
-compile(export_all). 

首先是模塊聲明,其中它的參數是一個原子(或,換句話說,一個小寫字,沒有引號)。從採取的瞭解你一些二郎

-module(Name).
這始終是一個文件的第一個屬性(和語句),並有很好的理由:它就是當前模塊,其中名稱是名一個原子。這是您用來從其他模塊調用函數的名稱。該電話與M:F(A)形式,其中M是模塊名稱,F功能上作出,並A的參數。

第二句話告訴你的編譯器,以使所有聲明的函數公共,每次你該模塊上寫功能F將能夠被外人稱爲pingpong:F
這可能會簡化您初次學習的過程,但通常是一種不好的做法。請參閱this question


讓我們來看看現在的功能。

start_pong() -> 
    register(pong, spawn(pingpong,pong,[])). 

這可能是您的代碼開始的地方。您編譯該模塊,然後在給定機器或節點的Erlang外殼中調用pingpong:start_pong().。所有此功能確實是「註冊名稱pong作爲我即將創建的過程的標識符,其中spawn「。

因此,spawn創建一個Erlang進程。 spawn也是一個內置函數(BIF),因此不需要預先指定其模塊名稱。它的論點是spawn(Module, Exported_Function, List of Arguments),見in the documentation
start_pong回首,它是所有「創建一個進程,通過該模塊在運行pong功能,不帶參數啓動,並調用過程乒乓


pong() -> 
    receive 
     finished -> 
      io:format("Pong finished ~n"); 
     {ping, Ping_Pid} -> 
      io:format("i am the receiver ~n"), 
     Ping_Pid ! pong, 
     pong() 
end. 

start_pong新創建的進程將運行該功能。Erlang的每個流程都有自己的郵箱。通過在這些郵箱中留下消息,進程可以相互通信。消息可能是幾乎什麼。把它們想象成你喜歡在進程之間發送的一些數據。

新鮮進程輸入receive語句,該語句告訴它從其郵箱中獲取消息,或者等到有一些消息。然後當收到消息時,它使用模式匹配來查找適當的操作。如果您習慣於使用命令式語言,請將其視爲switch,否則請忽略此語句。

如果該過程有單原子finished的消息,它會在控制檯中打印Pong finished並退出。
如果過程具有一條消息,是與原子ping進程標識符PID - 每個過程有一個)的一對,那麼它會執行的函數的剩下的代碼。

大寫Ping_Pid告訴Erlang將該消息具有的第二個值分配給名稱爲Ping_Pid的變量。恰巧你期待pid
它在輸入這種情況下的功能是打印i am the receiver,然後將原子pong的消息發送到由Ping_Pid標識的進程 - 這就是!運算符的用途。 Finnaly函數自動調用,以便再次查看郵箱。


你會在控制檯上寫的,可能是另一個節點/臺機器上的下一件事,會調用start_ping

start_ping(Pong_Node) -> 
    spawn(pingpong, ping, [3, Pong_Node]). 

正如我們之前看到的,這一切都被創建將要運行的ping功能的過程中,隨着參數3和它接收到的Pong_Node,這是其中第一個進程正在運行的機器(節點) 。


ping(0, Pong_Node) -> 
    {pong, Pong_Node} ! finished, 
    io:format("Pong finished ~n"); 

ping(N, Pong_Node) -> 
    {pong, Pong_Node} ! {ping, self()}, 
    receive 
     pong -> 
      io:format("i am the sender ~n") 
    end, 
    ping(N-1,Pong_Node). 

此功能在以下兩種情況定義的(請注意,第一ping塊與;,而不是.結束 - 這告訴二郎,有更多定義函數)。

你可以用3作爲第一個參數。由於30不匹配,因此該過程執行第二種情況,其中N作爲其參數。

該過程將對{ping, self()}發送到{pong, Pong_Node}給出的過程,該過程遵循語法{registered_name, node_name}self()用於檢索當前進程自己的pid
之後,該過程等待pong響應,並再次重複此操作,而N大於零。

N達到零時,執行第一種情況,發送finished{pong, Pong_Node},並結束執行。


如果你覺得這種解釋是不完整的,你也可以看看at the tutorial,展示了這個確切的方案。

+2

好的回答,花了一些時間寫下來,我相信! –