演员模型

在電腦科學中，**演員模型**（英語：Actor model）是一種並行運算上的模型。「演員」是一種程式上的抽象概念，被視為並行運算的基本單元：當一個演員接收到一則訊息，它可以做出一些決策、建立更多的演員、傳送更多的訊息、決定要如何回答接下來的訊息。演員可以修改它們自己的私有狀態，但是只能通過訊息間接的相互影響（避免了基於鎖的同步）。

演員模型在 1973 年於 Carl Hewitt、Peter Bishop 及 Richard Steiger 的論文中提出。它已經被用作並行計算的理論理解框架和並行系統的實際實現基礎。演員模型和其他類似工作的關係討論可見於演員模型和行程演算。

## 基本概念

演員模型推崇的哲學是「一切皆是演員」，這與物件導向程式設計的「一切皆是物件」類似。

演員是一個運算實體，回應接收到的訊息，相互間是並行的：

* 傳送有限數量的訊息給其他演員；
* 建立有限數量的新演員；
* 指定接收到下一個訊息時要用到的行為。

以上動作不含有順序執行的假設，因此可以並列進行。

傳送者與已傳送通信的解耦，是演員模型的根本優勢，演員模型啟用了非同步通信並將控制結構當作訊息傳遞的模式。

訊息接收者是通過位址區分的，有時也被稱作「郵件位址」。因此演員只能和它擁有位址的演員通信。它可以通過接收到的資訊取得位址，或者取得它建立的演員的位址。

演員模型的特徵是，演員內部或相互之間的計算本質上是並行性的，演員可以動態建立，演員位址包含在訊息中，互動只有通過直接的非同步訊息傳遞通信，不限制訊息到達的順序。

## 歷史

演員模型受到了 Lisp、Simula、Smalltalk-72、基於權限的系統和封包交換的影響。其發展「受到由幾十、幾百、甚至幾千個獨立微處理機構成的高度平行計算機器的前景所推動，其中的每個處理機都有自己局部記憶體和通信處理器，它們通過高效能網路進行通信。」此後隨著採用多核和眾核電腦架構的大規模並行計算的出現，人們已經重新燃起了對演員模型的興趣。

在 Hewitt、Bishop 和 Steiger 的 1973 年刊物之後，Irene Greif 在 1975 年博士論文中，為演員模型開發出了一種操作語意。Henry Baker 和 Hewitt 在 1977 年發表了演員系統的公理法則。其他主要的里程碑包括：William Clinger 的 1981 年學位論文，它介入了基於冪域的指稱語意；還有 Gul Agha 的 1985 年學位論文，它進一步發展出基於 transition 的語意模型，從而補充了 Clinger 的模型。這些工作促成了演員模型理論的全面發展。

主要的軟體實現工作，由麻省理工學院的訊息傳遞語意小組完成，其成員包括 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 人工智慧實驗室、斯坦福國際研究所、史丹福大學、伊利諾伊大學厄巴納 - 香檳分校、巴黎第六大學、比薩大學、東京大學米澤研究室、荷蘭數學和電腦科學研究學會和其他一些地方。

## 使用演員模型編程

一些程式語言使用了演員模型或變種。這些語言套件括：

### 早期的演員模型程式語言

### 後期的演員模型程式語言

### 演員模型庫及框架

演員模型庫及框架，允許使用者在沒有內建演員模型的語言中進行編程。這些框架包括：

注意這裡沒有列出全部框架和庫。

## 並行程式語言用例

儘管 Erlang 語言設計者並未如此表述，因其行程間通訊是通過無共享非同步訊息傳遞系統運作，它一般被引證為採用演員模型的典型代表之一。在 Erlang 中，所有行程都有一個自己的「電子信箱」，它是從其他行程已經傳送過來而仍未被消費的訊息的佇列。行程使用 `receive` 原語來檢索匹配預期模式的訊息。一個訊息處理常式針對每個模式依次測試這些訊息，直到其中有一個匹配。在訊息被消費並從電子信箱中移除之時行程恢復執行。訊息可以包含任何 Erlang 結構，包括原始類型（整數，浮點數、字元、原子）、元組、列表和函式。

下面例子展示了 Erlang 對分散式行程的內建支援：

## 原型的演員程式語言

Hewitt 在 2006 年發表了一個原型的演員程式語言，用意在於直接表達演員行為的重要方面。訊息採用如下表示法：

　
: `<標籤>[<元素>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]`，而且
1. 如果 `bindingCustomer` 收到 `Returned[environment』]`，傳送給 `<表達式>i` 一個 `Request[Eval[environment』]]`
2. 不然如果 `bindingCustomer` 收到 `Thrown[...]`，嘗試 `<模式>i+1`。

**_behavior_ ... <模式>i <表達式>i ...**
: 在收到 `Request[Eval[environment] customer]` 的時候，傳送給 customer 一個新演員 `theReceiver`，使得

　
: 在 `theReceiver` 收到 `Request[message customer』]` 的時候，建立一個新演員 `bindingCustomer`，並行送給 `environment` 一個 `Request [bind [< 模式>i message] customer』]`，而且
1. 如果 `bindingCustomer` 收到 `Returned[environment』]`，傳送給 `<表達式>i` 一個 `Request[Eval[environment』] customer』]`
2. 不然如果 `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)]

上述指令碼將建立一個儲存儲存格，它採用的行為 ReadWrite 定義如下：

　
: ReadWrite(contents) ≡

　
: _behavior_

　
: Request[read[] customer]

　
: {_send_ customer Returned[contents], Returned[ReadWrite(contents)]}

　
: Request[write[x] customer]

　
: {_send_ customer Returned[], Returned[ReadWrite(x)]}

例如，下列表達式建立一個儲存格 x，具有初始內容 5，並接著並行的向它寫值 7 和 9。

　
: _let_ x = Cell.Create[5] _in_ {x.write[7], x.write[9], x.read[]}

上述表達式的值是 5、7 或 9。

## 影響

演員模型在並行計算的理論發展和實踐軟體開發中都有影響。

### 理論

演員模型影響了 π- 演算和隨後的行程演算的發展。在 Robin Milner 的圖靈獎獲獎演說中，他寫到：

> 純 lambda 演算現在只使用兩種東西來建造：項和變數。我們在行程演算上也能實現同樣的經濟性嗎？Carl Hewitt 憑藉其演員模型，很久以前就應對了這個挑戰；他宣告了值、在值上的算子和行程，都應該是同一種東西：即演員。
> 
> 這個目標打動了我，因為它蘊涵了表達式有著同質性和完整性 ... 但是很久以後我才明白了如何依據代數演算來達成這個目標 ...
> 
> 因此本著 Hewitt 的精神，我們的第一步，就是要求由項指示或由名字存取的所有東西，包括值、暫存器、算子、行程、物件，都是同一種東西；它們都應當是行程。
> 
> 

### 實踐

演員模型在商業實踐中已經有了巨大的影響。例如，Twitter 將演員用於可伸縮性應用。還有，Microsoft 在其開發的非同步代理庫中使用了演員模型。

## 參見

* 資料流程編程
* 並列編程模型
* 輸入 / 輸出自動機
* 科學共同體隱喻

## 參照

## 延伸閱讀

## 外部連結