2010-07-24 104 views
12

Gul Agha在他的技術報告「演員:分佈式系統中的併發計算模型」中很好地描述了演員模型。如何在支持演員模型的語言中實現「成爲」?

在第49頁,他解釋了「變成」命令:

become <expression> 

叫「成爲X」,演員會將他所有的信息轉發到其他演員的郵箱(X)之後。

但是,我不確定這是如何在Erlang和Scala等語言中實現的(它實現了)。是否有必要手動編碼?效率如何? Agha使用消息傳遞展示了一個堆棧的實現。每次執行彈出或推送時,都會向某個參與者添加一個轉發鏈接...經過數十萬次操作後,我希望這樣的實現花費太多時間轉發消息,而不是進行實際的工作,除非某些引擎蓋下進行了很好的優化。

所以我的問題是:如何在典型的演員語言如Erlang,Scala(和其他語言的庫)中實現轉發(或「成爲」)?

回答

3

請參閱Agha論文的第12頁(第26頁)Agha的論文「參與者:分佈式系統中併發計算的模型」。 「成爲」是他的演員語言是你如何指定#2,演員的新行爲。將消息轉發給其他角色只是許多可能的新行爲之一。

我認爲,如果你想要轉發行爲,與斯卡拉演員相比,你與Erlang基本上是同一條船。在引擎蓋下,斯卡拉「反應」和「反應在內」的工作非常像,因爲反應塊定義的部分功能是演員的新行爲,但我不確定相似性甚至是有意的。

大多數(所有?)「演員」實現偏離了休伊特的演員模型和阿迦的演員語言。 IIRC從Agha的語言中指定演員行爲的語言部分甚至都不是圖靈完整的。當你考慮演員的配置時,整個語言只會變成圖靈完整的。我會說演員模型和當前演員框架之間的關係有點像SmallTalk中的面向對象與C++中的面向對象的關係。有一些概念轉移和類似的術語,但在細節上它們非常非常不同。

7

它不直接用Erlang實現,但你可以寫一個簡單的become函數接收消息時,將其轉發給另一個進程,然後調用本身:

become(Pid) -> 
    receive 
     Msg -> Pid ! Msg 
    end, 
    become(Pid). 

(一種工業強度的版本這可能需要處理信號和其他的可能性,但這是它的本質)。

調用become(Pid)將從外部世界的角度有效地將調用過程轉變爲過程Pid

這並沒有解決您突出顯示的問題,重複呼叫become導致轉發鏈增長。然而,這在Erlang中通常不會發生,我不確定Erlang的進程如何映射到Actor模型。

4

演員是一個逆變共函件,所以「成爲」只是comap。換句話說,T類型消息上的Actor基本上是類型(T => Unit)的函數。這只是功能組合(可能帶有身份功能)。

它在Scalaz實施:

val a1 = actor(a => println(a)) 
val a2 = a1.comap(f) 

演員A2將f到它的消息,然後將結果發送到A 1。

+0

函數組合語義看起來與這個頁面上的* become *的其他描述不一致,我讀這個函數說的是一個actor的語義替換另一個的,而不是添加到它。 – 2010-07-25 14:21:31

+0

好的,將新的actor分配給相同的變量。 – Apocalisp 2010-07-25 15:46:32

4

在這裏尋找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!"}. 

所以當AB預期的消息,這將只能夠匹配這些使用模式如{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),以避免在未來與熱代碼加載潛在的崩潰。 *

3

Akka的參與者有一個「HotSwap」概念,您可以將新的PartialFunction發送給替換其現有消息處理程序的Actor。前一個被記住並且可以被恢復。有關詳細信息,請在http://doc.akkasource.org/actors上搜索「hotswap」。

+1

另外,在未發佈的0.10版本中,「成爲」: http://github.com/jboner/akka/blob/master/akka-core/src/main/scala/actor/Actor.scala#L421 – 2010-07-26 11:13:09