2017-11-03 111 views
0

我的模塊中有變量,並且有更新變量值的接收方法。多個進程同時調用這個方法。當一個進程正在修改它時,我需要鎖定這個變量。示例如下有沒有辦法在Erlang中的不同進程之間鎖定變量

mytest.erl

%%%------------------------------------------------------------------- 
-module(mytest). 

%% API 
-export([start_link/0,display/1,callDisplay/2]). 

start_link()-> 
    Pid=spawn(mytest,display,["Hello"]), 
    Pid. 

display(Val) -> 
    io:format("It started: ~p",[Val]), 
    NextVal= 
    receive 
     {call,Msg}-> 
     NewVal=Val++" "++Msg++" ", 
     NewVal; 
     stop-> 
     true 
    end, 
    display(NextVal). 

callDisplay(Pid,Val)-> 
    Pid!{call,Val}. 

啓動它

Pid=mytest:start_link(). 

兩個過程都在同一時間

P1=spawn(mytest,callDisplay,[Pid,"Walter"]), 
P2=spawn(mytest,callDisplay,[Pid,"Dave"]). 

我希望它可以添加 「沃爾特」 叫它,「戴夫」一個接一個地像「你好,沃爾特戴夫」,然而,當他們中有太多人一起跑步時,一些名字(Walter,Dave,e TC)將被覆蓋。

因爲當P1,P2同時啓動時,Val都是「你好」。 P1添加「Walter」成爲「Hello Walter」,P2添加「Dave」成爲「Hello Dave」。 P1將它首先保存爲NextVal作爲「Hello Walter」,然後P2將它保存爲NextVal作爲「Hello Dave」,因此結果將爲「Hello Dave」。 「你好,沃爾特」被「你好戴夫」取代,「沃爾特」永遠失去了。

有什麼辦法可以鎖定「Val」,所以當我們添加「Walter」時,「Dave」會等待Value設置完成嗎?

+5

進程可以一次只處理一個消息。消息按順序處理,而不是同時處理。這不應該發生。你能發佈完整的代碼來重現這種行爲嗎? – Dogbert

+0

可能是客戶的問題。是否有任何'callDisplay'進程發出錯誤,例如超時? –

回答

3

即使這是一個老問題,但它是值得解釋的。 從你說的話,如果我是正確的, 你希望看到

「你好沃爾特」和「你好戴夫」。但是,你看到連續的名字被添加到前者爲,「你好沃爾特戴夫。」

這種現象是正常的,一看就知道讓在二郎內存模型簡單瞭解一下。 Erlang進程存儲器分爲三個主要部分:

進程控制塊(PCB): 此保持進程pid,註冊名稱,表,狀態和指針信息在它的隊列中。

棧: 這個hold函數參數,局部變量和函數返回地址。

私有堆:這可以保留傳入的消息複合數據,如元組,列表和二進制(不大於64字節)。

這些內存中的所有數據都屬於擁有進程的私有進程。

階段1:

Pid=spawn(mytest,display,["Hello"])被調用時,在創建服務器進程,然後與顯示功能的「Hello」傳遞作爲參數被調用。由於display/1在服務進程中執行,"Hello"參數位於服務器的進程堆棧中。繼續執行display/1,直至達到receive子句,然後阻止並等待與您的格式匹配的消息。

第2階段:

現在P1啓動時,它執行ServerPid ! {call, "Walter"},然後P2執行ServerPid ! {call, "Dave"}。在這兩種情況下,erlang複製消息並將其發送到服務器的進程郵箱(私有堆)。郵箱中的此複製郵件屬於服務器進程而不屬於客戶端。 現在,當{call, "Walter"}匹配,Msg獲取綁定到"Walter"。 從階段1,我們知道Val是有界的"Hello",Newval然後得到界限爲
"Val ++ " " ++ Msg" = "Hello Walter"

在這一點上,P2的消息,{call, "Dave"},仍然是在服務器的郵箱,等待下一次receive條款,將在接下來的遞歸調用發生display/1NextVal獲取綁定到NewValdispaly/1"Hello Walter"作爲參數傳遞的遞歸調用。這給人的第一印"Hello Walter "現在也住在服務器的進程堆棧

現在當receive子句再次到達時,P2的消息{call, "Dave"}被匹配。 現在NewValNextVal被綁定到"Hello Walter" ++ " " ++ "Dave" = "Hello Walter Dave".這將作爲參數傳遞給display/1作爲新的Val來打印Hello Walter Dave。簡而言之,該變量在每個服務器循環中都會更新。它的作用與State術語gen_server行爲相同。在你的情況下,連續的客戶端調用只是將消息附加到此服務狀態變量。現在,你的問題,

有什麼辦法,我可以鎖定Val,所以當我們添加"Walter""Dave"會等到值設置完成?

不可以鎖定。 Erlang不以這種方式工作。
沒有過程鎖定結構,因爲它不需要過程鎖定結構。 數據(變量)始終不變的,私人(除了大的二進制文件,其停留在共享堆),以創建它的過程。 此外,它不是您在接收過程中處理的Pid ! Msg構造中使用的實際消息。它是複製品。在您的display/1功能的Val參數是私有的,屬於服務器的過程,因爲它生活在它內存爲display/1每次調用是由服務器進程本身造成的。因此,任何其他進程都無法鎖定,甚至無法看到該變量。

是。通過順序消息處理 這正是服務器進程正在做的事情。從隊列中一次輪詢一條消息。當{call, "Walter"}被帶走時,{call, "Dave"}正在隊列中等待。爲什麼你會看到意外的問候語的原因是因爲你改變了服務器的狀態下,display/1參數爲下display/1呼叫,其處理{call, "Dave"}

相關問題