在這裏尋找Erlang。
在基本水平上,有兩個選項可用。如果您只想使用become
來更改給定進程的行爲(請參見第2.1.3節列表中的第2點),那麼這只是調用具有不同遞歸函數的下一個循環的問題:
loop(State) ->
receive
{normal, Msg} -> loop(State);
{change, NewLoop} -> NewLoop(State)
end.
假設NewLoop
是一個高階函數,只要你發送郵件{change, NewLoop}
的過程開始運行功能loop/1
,它將使用NewLoop
作爲其定義。
第二個選項是您希望進程充當代理(並更改行爲)的選項。這與Marcelo Cantos的建議類似。只是有循環處理和轉發郵件到一個新的(偷了他的代碼):
become(Pid) ->
receive
Msg -> Pid ! Msg
end,
become(Pid).
從理論上講,這確實紙會問什麼。但實際上,在現實生活中的Erlang系統中使用第二種選擇存在風險。在兩個進程之間的通信中,將郵件與發件人的進程標識符進行「標記」是一個常見的概念,並且回覆將標記爲接收方的進程標識符。以下信息可以做的交換(這不是代碼,只需一隻手符號):
A = <0.42.0> <-- process identifier
B = <0.54.0>,
A: {A, "hello"},
B: {B, "hi"},
A: {A, "how are you?"}.
B: {B, "Fine!"}.
所以當A
從B
預期的消息,這將只能夠匹配這些使用模式如{B, Message}
。在轉發消息的情況下,這種尋址方案變得無效並且簡單地中斷。
另一種方法是使用引用(make_ref()
)作爲尋址方案來匹配返回的Pid頂部的消息。這將通過使用兩個不同的實體來分開使用Pid作爲地址和標識符。
還有一個問題,即使尋址是安全的:過程依賴關係。命名進程,崩潰進程,監視器等會發生什麼?客戶端進程可能有監視器,鏈接和未設置的所有設置,以確保沒有通知就沒有任何問題。通過修改路由過程中捕獲退出信號併發送,應該有可能使事情變得更安全:
loop(State) ->
receive
{normal, Msg} -> loop(State);
{become, Pid} ->
process_flag(trap_exit, true), % catch exit signals as messages
link(Pid), % make it so if one process crashes, the other receives the signal
become(Pid)
end.
become(Pid) ->
receive
{'EXIT', Pid, killed} -> exit(self(), kill); %% uncatchable termination
{'EXIT', Pid, normal} -> ok; %% die normally too
{'EXIT', Pid, Reason} -> exit(Reason); %% die for the same reason as Pid
Msg -> Pid ! Msg %% forward the message
end,
become(Pid).
此測試的代碼應該是更加安全取決於第一個進程的過程會得到同樣的錯誤消息的在become(Pid)
中由Pid
表示的那個,使得路由相當透明。但是,我不會保證這將在實際應用中長期運行。
儘管Erlang的標準庫在描述和執行諸如become
之類的東西時可能並且在概念上很簡單,但它並沒有真正考慮到第二個用例。對於真實世界的應用程序,我只能推薦第一種方法,現在每個Erlang應用程序都廣泛使用這種方法。第二個是罕見的,可能會導致問題
* *來電來在最後一個例子功能become/1
可能應?MODULE:become(Pid)
,以避免在未來與熱代碼加載潛在的崩潰。 *
函數組合語義看起來與這個頁面上的* become *的其他描述不一致,我讀這個函數說的是一個actor的語義替換另一個的,而不是添加到它。 – 2010-07-25 14:21:31
好的,將新的actor分配給相同的變量。 – Apocalisp 2010-07-25 15:46:32