演員模型
来自维基百科,自由的百科全书
在電腦科學中,演員模型(英語:Actor model)是一種並行運算上的模型。「演員」是一種程式上的抽象概念,被視為並行運算的基本單元:當一個演員接收到一則訊息,它可以做出一些決策、建立更多的演員、傳送更多的訊息、決定要如何回答接下來的訊息。演員可以修改它們自己的私有狀態,但是只能通過訊息間接的相互影響(避免了基於鎖的同步)。
演員模型在1973年於Carl Hewitt、Peter Bishop及Richard Steiger的論文中提出[1]。它已經被用作並行計算的理論理解框架和並行系統的實際實現基礎。演員模型和其他類似工作的關係討論可見於演員模型和行程演算。
基本概念
演員模型推崇的哲學是「一切皆是演員」,這與物件導向程式設計的「一切皆是對象」類似。
演員是一個運算實體,回應接收到的訊息,相互間是並行的:
- 傳送有限數量的訊息給其他演員;
- 建立有限數量的新演員;
- 指定接收到下一個訊息時要用到的行為。
以上動作不含有順序執行的假設,因此可以並列進行。
傳送者與已傳送通訊的解耦,是演員模型的根本優勢,演員模型啟用了非同步通訊並將控制結構當作訊息傳遞的模式[2]。
訊息接收者是通過位址區分的,有時也被稱作「郵件位址」。因此演員只能和它擁有位址的演員通訊。它可以通過接收到的資訊取得位址,或者取得它建立的演員的位址。
演員模型的特徵是,演員內部或相互之間的計算本質上是並行性的,演員可以動態建立,演員位址包含在訊息中,互動只有通過直接的非同步訊息傳遞通訊,不限制訊息到達的順序。
歷史
演員模型受到了Lisp、Simula、Smalltalk-72、基於權限的系統和封包交換的影響。其發展「受到由幾十、幾百、甚至幾千個獨立微處理機構成的高度平行計算機器的前景所推動,其中的每個處理機都有自己局部主記憶體和通訊處理器,它們通過高效能網路進行通訊。」[3]此後隨著採用多核和眾核電腦架構的大規模並行計算的出現,人們已經重新燃起了對演員模型的興趣。
在Hewitt、Bishop和Steiger的1973年刊物之後,Irene Greif在1975年博士論文中,為演員模型開發出了一種操作語意[4]。Henry Baker和Hewitt在1977年發表了演員系統的公理法則[5][6]。其他主要的里程碑包括:William Clinger的1981年學位論文,它介入了基於冪域的指稱語意[3];還有Gul Agha的1985年學位論文,它進一步發展出基於transition的語意模型,從而補充了Clinger的模型[7]。這些工作促成了演員模型理論的全面發展。
主要的軟體實現工作,由麻省理工學院的訊息傳遞語意小組完成,其成員包括Russ Atkinson、Giuseppe Attardi、Henry Baker、Gerry Barber、Peter Bishop、Peter de Jong、Ken Kahn、Henry Lieberman、Carl Manning、Tom Reinhardt、Richard Steiger和Dan Theriault。分別由加州理工學院的Chuck Seitz和麻省理工學院的Bill Dally領導的研究小組,致力於構造新的電腦架構,用以進一步發展演員模型中的訊息傳遞。有關工作詳見演員模型實現。
演員模型的研究,已經開展於加州理工學院、京都大學、微電子及電腦技術公司、MIT人工智慧實驗室、斯坦福國際研究所、史丹佛大學、伊利諾伊大學厄巴納-香檳分校[8]、巴黎第六大學、比薩大學、東京大學米澤研究室、荷蘭數學和電腦科學研究學會和其他一些地方。
使用演員模型編程
一些程式語言使用了演員模型或變種。這些語言套件括:
演員模型庫及框架,允許使用者在沒有內建演員模型的語言中進行編程。這些框架包括:
名稱 | 狀態 | 最新發行 | 許可證 | 語言 |
---|---|---|---|---|
ReActed[26] | 活躍 | 2022-11-30 | Apache 2.0 | Java |
Acteur[27] | 活躍 | 2020-04-16[28] | Apache-2.0 / MIT | Rust |
Bastion[29] | 活躍 | 2020-08-12[30] | Apache-2.0 / MIT | Rust |
Actix[31] | 活躍 | 2019-05-30[32] | MIT | Rust |
Aojet[33] | 活躍 | 2016-10-17 | MIT | Swift |
Actor[34] | 活躍 | 2017-03-09 | MIT | Java |
Actor4j[35] | 活躍 | 2020-01-31 | Apache 2.0 | Java |
Actr[36] | 活躍 | 2019-04-09[37] | Apache 2.0 | Java |
Vert.x[38] | 活躍 | 2018-02-13 | Apache 2.0 | Java, Groovy, Javascript, Ruby, Scala, Kotlin, Ceylon |
ActorFx[39] | 不活躍 | 2013-11-13 | Apache 2.0 | .NET |
Akka | 活躍 | 2022-09-06[40] | Apache 2.0 | Java and Scala |
Akka.NET[41] | 活躍 | 2020-08-20[42] | Apache 2.0 | .NET |
Apache Pekko[43] | 活躍 | 2023-07-26[44] | Apache 2.0 | Java and Scala |
Remact.Net[45] | 不活躍 | 2016-06-26 | MIT | .NET, Javascript |
Ateji PX[46] | 不活躍 | ? | ? | Java |
czmq[47] | 活躍 | 2016-11-10 | MPL-2 | C |
F# MailboxProcessor | 活躍 | 同於F# (內建核心庫) | Apache License | F# |
Korus[48] | 活躍 | 2010-02-04 | GPL 3 | Java |
Kilim[49][50] | 活躍 | 2018-11-09[51] | MIT | Java |
ActorFoundry (基於Kilim) | 不活躍 | 2008-12-28 | ? | Java |
ActorKit[52] | 活躍 | 2011-09-13[53] | BSD | Objective-C |
Cloud Haskell[54] | 活躍 | 2015-06-17[55] | BSD | Haskell |
CloudI[56] | 活躍 | 2023-10-27[57] | MIT | ATS, C/C++, Elixir/Erlang/LFE, Go, Haskell, Java, Javascript, OCaml, Perl, PHP, Python, Ruby |
Clutter[58] | 活躍 | 2017-05-12[59] | LGPL 2.1 | C, C++ (cluttermm), Python (pyclutter), Perl (perl-Clutter) |
NAct[60] | 不活躍 | 2012-02-28 | LGPL 3.0 | .NET |
Nact[61] | 活躍 | 2018-06-06[62] | Apache 2.0 | JavaScript/ReasonML |
Retlang[63] | 不活躍 | 2011-05-18[64] | New BSD | .NET |
JActor[65] | 不活躍 | 2013-01-22 | LGPL | Java |
Jetlang[66] | 活躍 | 2013-05-30[67] | New BSD | Java |
Haskell-Actor[68] | 不活躍? | 2008 | New BSD | Haskell |
GPars[69] | 活躍 | 2014-05-09[70] | Apache 2.0 | Groovy |
OOSMOS[71] | 活躍 | 2019-05-09[72] | GPL 2.0和商業(雙許可證) | C. C++ friendly |
Panini[73] | 活躍 | 2014-05-22 | MPL 1.1 | 自己的程式語言 |
PARLEY[74] | 不活躍? | 2007-22-07 | GPL 2.1 | Python |
Peernetic[75] | 活躍 | 2007-06-29 | LGPL 3.0 | Java |
PostSharp[76] | 活躍 | 2014-09-24 | 商業 / Freemium | .NET |
Pulsar[77] | 活躍 | 2016-07-09[78] | New BSD | Python |
Pulsar[79] | 活躍 | 2016-02-18[80] | LGPL/Eclipse | Clojure |
Pykka[81] | 活躍 | 2019-05-07[82] | Apache 2.0 | Python |
Termite Scheme[83] | 不活躍? | 2009-05-21 | LGPL | Scheme (Gambit實現) |
Theron[84] | 不活躍[85] | 2014-01-18[86] | MIT[87] | C++ |
Thespian[88] | 活躍 | 2020-03-10 | MIT | Python |
Quasar[89] | 活躍 | 2018-11-02[90] | LGPL/Eclipse | Java |
Libactor[91] | 不活躍? | 2009 | GPL 2.0 | C |
Actor-CPP[92] | 活躍 | 2012-03-10[93] | GPL 2.0 | C++ |
S4[94] | 不活躍 | 2012-07-31[95] | Apache 2.0 | Java |
C++ Actor Framework (CAF)[96] | 活躍 | 2020-02-08[97] | Boost Software License 1.0 and BSD 3-Clause | C++11 |
Celluloid[98] | 活躍 | 2018-12-20[99] | MIT | Ruby |
LabVIEW Actor Framework[100] | 活躍 | 2012-03-01[101] | National Instruments SLA[102] | LabVIEW |
LabVIEW Messenger Library[103] | 活躍 | 2021-05-24 | BSD | LabVIEW |
Otavia[104] | 活躍 | 2024-01-02 | Apache 2.0 | Scala |
Orbit[105] | 活躍 | 2019-05-28[106] | New BSD | Java |
QP框架 | 活躍 | 2019-05-25[107] | GPL 2.0和商業(雙許可證) | C and C++ |
libprocess[108] | 活躍 | 2013-06-19 | Apache 2.0 | C++ |
SObjectizer[109] | 活躍 | 2021-12-28[110] | New BSD | C++11 |
rotor[111] | 活躍 | 2022-04-23[112] | MIT License | C++17 |
Orleans[113] | 活躍 | 2023-07-11[114] | MIT License | C#/.NET |
Skynet[115] | 活躍 | 2016-07-11 | MIT License | C/Lua |
Reactors.IO[116] | 活躍 | 2016-06-14 | BSD License | Java/Scala |
libagents[117] | 活躍 | 2020-03-08 | Free software license | C++11 |
Proto.Actor[118] | 活躍 | 2021-01-05 | Free software license | Go, C#, Python, JavaScript, Java, Kotlin |
FunctionalJava[119] | 活躍 | 2018-08-18[120] | BSD 3-Clause | Java |
Riker[121] | 活躍 | 2019-01-04 | MIT License | Rust |
Comedy[122] | 活躍 | 2019-03-09 | EPL 1.0 | JavaScript |
VLINGO XOOM Actors[123] | 活躍 | 2023-02-15 | Mozilla Public License 2.0 | Java, Kotlin, JVM languages, C# .NET |
wasmCloud[124] | 活躍 | 2021-03-23 | Apache 2.0 | WebAssembly (Rust, TinyGo, Zig, AssemblyScript) |
ray[125] | 活躍 | 2020-08-27 | Apache 2.0 | Python |
DOTNETACTORS[126] | 活躍 | 2021-06-14 | MIT | .NET, C#, Azure Service Bus |
go-actor[127] | 活躍 | 2022-08-16 | GPL 3.0 | Golang |
Sento[128] | 活躍 | 2022-11-21 | Apache 2.0 | Common Lisp |
Xcraft Goblins[129] | 活躍 | 2022-08-30 | MIT | JavaScript |
Tarant[130] | 活躍 | 2023-04-17 | MIT | Typescript, Javascript |
注意這裡沒有列出全部框架和庫。
並行程式語言用例
儘管Erlang語言設計者並未如此表述[131],因其行程間通訊是通過無共享非同步訊息傳遞系統運作,它一般被引證為採用演員模型的典型代表之一。在Erlang中,所有行程都有一個自己的「電子信箱」,它是從其他行程已經傳送過來而仍未被消費的訊息的佇列。行程使用receive
原語來檢索匹配預期模式的訊息。一個訊息處理常式針對每個模式依次測試這些訊息,直到其中有一個匹配。在訊息被消費並從電子信箱中移除之時行程恢復執行。訊息可以包含任何Erlang結構,包括原始類型(整數,浮點數、字元、原子)、元組、列表和函式。
下面例子展示了Erlang對分散式行程的內建支援:
% 建立一个进程并启用函数web:start_server(Port, MaxConnections)
ServerProcess = spawn(web, start_server, [Port, MaxConnections]),
% 在机器RemoteNode上建立一个远程进程并启用函数web:start_server(Port, MaxConnections)
RemoteProcess = spawn(RemoteNode, web, start_server, [Port, MaxConnections]),
% (异步的)发送消息到ServerProcess。消息包含一个元组,它具有原子"pause"和数"10"。
ServerProcess ! {pause, 10},
% 接收发给这个进程的消息
receive
a_message -> do_something;
{data, DataContent} -> handle(DataContent);
{hello, Text} -> io:format("Got hello message: ~s", [Text]);
{goodbye, Text} -> io:format("Got goodbye message: ~s", [Text])
end.
原型的演員程式語言
Hewitt在2006年發表了一個原型的演員程式語言,用意在於直接表達演員行為的重要方面[132]。訊息採用如下表示法:
- <標籤>[<元素>1 ... <元素>n]
程式語言的語意是通過將每個程式構造確定為有自己行為的演員來定義的。執行是通過在執行期間讓Eval訊息在程式構造之間傳遞來建模的。
每個Eval訊息都有一個充當環境的演員的位址,它能夠進行識別碼與值的繫結(binding)。environment演員是不可變的(immutable),也就是不變更的。
- 當一個environment演員收到Request[Bind[identifier value] customer]的時候,建立一個新的環境演員environment』傳送給customer,使得這個新環境演員收到Request[Lookup[identifier』] customer』]的時候,如果identifier同於identifier』,則傳送給customer』一個Returned[value],否則傳送給environment一個Request[Lookup[identifier』] customer』]。
- 當一個environment演員收到Request[Bind[<模式> String] customer]的時候,如果此<模式>形如Request[msg[paramerer] customer],匹配於String形如Request[msg[argument] customer],則建立一個新的環境演員environment』傳送給customer,使得這個新環境演員收到Request[Lookup[parameter』] customer』]的時候,如果parameter』同於parameter,則傳送給customer』一個Returned[argument],否則傳送給customer一個Thrown[NotFound[<模式>]]。
- 當一個environment演員收到Request[Bind[identifier(parameter) value] customer]的時候,建立一個新的環境演員environment』傳送給customer,使得這個新環境演員收到Request[Lookup[identifier』(argument)] customer』]的時候,如果identifier同於identifier』,則建立一個新的環境演員environment』』,傳送給customer』一個Returned[value]和一個Returned[environment』』],否則傳送給environment一個Request[Lookup[identifier』(argument)] customer』]。這個新環境演員environment』』在收到Request[Lookup[parameter』] customer』]的時候,如果parameter』同於parameter,則傳送給customer』一個Returned[argument],否則傳送給environment』一個Request[Lookup[parameter』] customer』]。
上述環境演員建造在EmptyEnvironment演員之上,它在接收到Request[Lookup[identifier] customer]的時候,傳送給customer一個Thrown[NotFound[identifier]]。當它收到Bind請求的時候,EmptyEnvironment表現的如同上述環境演員。
原型語言有如下種類的表達式,這裡的通訊包括Request[...]、Returned[...]和Thrown[...],這裡的訊息包括Eval[...]、Bind[...]和Lookup[...]:
- <識別碼>
- 在收到Request[Eval[environment] customer]的時候,傳送給environment一個Request[Lookup[<識別碼>] customer]。
- send <接收者> <通訊>
- 在收到Request[Eval[environment] customer]的時候,建立一個新演員evalCustomer1,傳送給<接收者>一個Request[Eval[environment] evalCustomer1],使得
- 在evalCustomer1收到通訊Returned[theRecipient]的時候,建立一個新演員evalCustomer2,傳送給<通訊>一個Request[Eval[environment] evalCustomer2],使得
- 在evalCustomer2收到通訊Returned[theCommunication]的時候,傳送給theRecipient一個theCommunication。
- <接收者>.<訊息>
- 在收到Request[Eval[environment] customer]的時候,建立一個新演員evalCustomer1,傳送<接收者>一個Request[Eval[environment] evalCustomer1],使得
- 在evalCustomer1收到通訊Returned[theRecipient]的時候,建立一個新演員evalCustomer2,傳送給<訊息>一個Request[Eval[environment] evalCustomer2],使得
- 在 evalCustomer2收到通訊Returned[theMessage]的時候,傳送給theRecipient一個Request[theMessage customer]。
- receiver ... <模式>i <表達式>i ...
- 在收到Request[Eval[environment] customer]的時候,傳送給customer一個新演員theReceiver,使得
- 在theReceiver收到通訊內容com的時候,建立一個新演員bindingCustomer,並行送給environment一個Request[Bind[<模式>i com] bindingCustomer],而且
- 如果bindingCustomer收到Returned[environment』],傳送給<表達式>i一個Request[Eval[environment』]]
- 不然如果bindingCustomer收到Thrown[...],嘗試<模式>i+1。
- behavior ... <模式>i <表達式>i ...
- 在收到Request[Eval[environment] customer]的時候,傳送給customer一個新演員theReceiver,使得
- 在theReceiver收到Request[message customer』]的時候,建立一個新演員bindingCustomer,並行送給environment一個Request[bind[<模式>i message] customer』],而且
- 如果bindingCustomer收到Returned[environment』],傳送給<表達式>i一個Request[Eval[environment』] customer』]
- 不然如果bindingCustomer收到Thrown[...],嘗試<模式>i+1。
- {<表達式>1, <表達式>2}
- 在收到Request[Eval[environment] customer]的時候,傳送給<表達式>1一個Request[Eval[environment]],而且並行的傳送給<表達式>2一個Request[Eval[environment] customer]。
- let <識別碼> = <表達式>值 in <表達式>體
- 在收到message[Eval[environment] customer]的時候,建立一個新演員evalCustomer,並行送給<表達式>值一個Request[Eval[environment] evalCustomer]。
- 在evalCustomer收到Returned[theValue]的時候,建立一個新演員bindingCustomer,並行送給environment一個Request[bind[<識別碼> theValue] bindingCustomer]。
- 在bindingCustomer收到Returned[environment』]的時候,傳送給<expression>體一個Request[Eval[environment』] customer]。
- serializer <表達式>
- 在收到Request[Eval[environment] customer]的時候,傳送給customer一個Returned[theSerializer],這裡的theSerializer是新演員,使得傳送到theSerializer的通訊按FIFO次序由行為演員處理,行為演員初始是<表達式>.Eval[environment],而且
- 在theSerializer收到通訊內容com的時候,建立一個新演員customer』,傳送給行為演員一個Request[com customer』],使得
- 在customer』收到Returned[value]和Returned[theNextBehavior]的時候,Returned[value]被傳送給customer,而theNextBehavior被theSerializer用作下次通訊的行為演員。
下面是簡單的儲存儲存格(cell)的例子指令碼(script),它可以包含任何演員位址:
- Cell ≡
- receiver
- Request[Create[initial] customer]
- send customer Returned[serializer ReadWrite(initial)]
- Request[Create[initial] customer]
- receiver
上述指令碼將建立一個儲存儲存格,它採用的行為ReadWrite定義如下:
- ReadWrite(contents) ≡
- behavior
- Request[read[] customer]
- {send customer Returned[contents], Returned[ReadWrite(contents)]}
- Request[write[x] customer]
- {send customer Returned[], Returned[ReadWrite(x)]}
- Request[read[] customer]
- behavior
例如,下列表達式建立一個儲存格x,具有初始內容5,並接著並行的向它寫值7和9。
- let x = Cell.Create[5] in {x.write[7], x.write[9], x.read[]}
上述表達式的值是5、7或9。
影響
演員模型在並行計算的理論發展和實踐軟體開發中都有影響。
演員模型影響了π-演算和隨後的行程演算的發展。在Robin Milner的圖靈獎獲獎演說中,他寫到[133]:
純lambda演算現在只使用兩種東西來建造:項和變數。我們在行程演算上也能實現同樣的經濟性嗎?Carl Hewitt憑藉其演員模型,很久以前就應對了這個挑戰;他宣告了值、在值上的算子和行程,都應該是同一種東西:即演員。
這個目標打動了我,因為它蘊涵了表達式有著同質性和完整性 ... 但是很久以後我才明白了如何依據代數演算來達成這個目標 ...
因此本著Hewitt的精神,我們的第一步,就是要求由項指示或由名字訪問的所有東西,包括值、暫存器、算子、行程、對象,都是同一種東西;它們都應當是行程。
演員模型在商業實踐中已經有了巨大的影響。例如,Twitter將演員用於可伸縮性應用[134]。還有,Microsoft在其開發的非同步代理庫中使用了演員模型[135]。
參見
參照
延伸閱讀
外部連結
Wikiwand - on
Seamless Wikipedia browsing. On steroids.