適合C# Actor的訊息執行方式(1):Erlang中的模式匹配
前言
Actor模型為並行而生。由於現在單臺機器中獨立的計算單元也越來越多,Actor模型的重要性也越來越大。Actor模型的理念非常簡單:天下萬物皆為Actor,Actor之間通過傳送訊息進行通訊。不同的Actor可以同時處理各自的訊息,從而獲得了大規模的併發能力。
Erlang基於Actor模型實現,我們甚至可以這樣認為,沒有Erlang在業界豎立的豐碑,Actor模型便不會如此受人關注。目前,幾乎所有的主流開發平臺上都有了Actor模型的實現,如Java平臺下的Jetlang以及.NET平臺下的MS CCR和Retlang;還有一些Actor框架專為特定語言設計,如F#的MailboxProcessor以及Scala的Actor類庫;甚至微軟還基於MS CCR構建了一門新的語言Axum。
不過對於.NET平臺下的開發人員來說,我們最常用的語言是C#。無論您是在使用MS CCR還是Retlang(亦或是我寫的ActorLite),在訊息的執行階段總是略顯尷尬。本文的目的便是提出一種適合C# Actor的訊息執行方式,而這種執行方式還會成為我以後公開的C#中“模式匹配”的基礎。
Erlang中的執行方式
本文將分為三個部分,您目前正在閱讀的第一部分,將會觀察Erlang是如何執行訊息的。有對比才會有差距,也正是由於Erlang在Actor模型上的示範作用,我們才會意識到C# Actor在使用上有多麼的不方便。
作為示例,我們還是使用最經典的乒乓測試。乒乓測試的效果很簡單:ping和pong為兩個Actor物件,首先由ping向pong傳送一個“Ping”訊息,pong在接受到Ping訊息之後,將會向ping傳送一個“Pong”訊息。在雙方“乒來乓去”幾個回合後,ping將會向pong發起“Finished”,從而停止互動。
乒乓測試的Erlang的實現程式碼如下:
-module(tut15). -export([start/0, ping/2, pong/0]). ping(0, Pong_PID) -> Pong_PID ! finished, io:format("ping finished~n", []); ping(N, Pong_PID) -> Pong_PID ! {ping, self()}, receive pong -> io:format("Ping received pong~n", []) end, ping(N - 1, Pong_PID). pong() -> receive finished -> io:format("Pong finished~n", []); {ping, Ping_PID} -> io:format("Pong received ping~n", []), Ping_PID ! pong, pong() end. start() -> Pong_PID = spawn(tut15, pong, []), spawn(tut15, ping, [3, Pong_PID]).
由於Erlang的函數語言程式設計,尾遞迴,receive原語等特殊的語言特性,其乒乓測試的實現或語義上可能和其他語言有一定區別(詳見《天下無處不乒乓》一文)。不過我們現在還是關注Erlang在訊息執行時的特性:模式匹配。
雖然Erlang有諸多優秀特性,但是它的資料抽象能力非常有限。在Erlang中常用的資料結構只有三種:
- 原子(atom):原子使用小寫開頭的識別符號來表示。您可以把原子認為是一種字串常量來看待,事實上它除了作為標識之外也沒有額外的作用。
- 繫結(binding):大寫開頭的標示符則為繫結,您可以近似地將其理解為“只能設定一次”的變數。一個繫結內部可以儲存任何資料,如一個程式(Erlang的概念,並非指作業系統程式)的id,一個數字,或一個字串。
- 元組(tuple):顧名思義,“元組”即為“單元的組合”,單元即為“原子”,“繫結”以及其他“元組”,通過某種方式結合起來。如上述程式碼中{ping, Ping_PID}便是一個由原子“ping”和繫結“Ping_PID”組成。當然您也可以寫成{do, {ping, Hello, World}, 7}這種巢狀的元組結構。
Erlang中的receive原語的作用是接受下一條訊息,直到有可用訊息時它才會執行下面的程式碼。Erlang使用了模式匹配(Pattern Matching)來表現接受到不同訊息時的邏輯分支。如pong的實現:
pong() -> receive finished -> io:format("Pong finished~n", []); {ping, Ping_PID} -> io:format("Pong received ping~n", []), Ping_PID ! pong, pong() end.
在這段程式碼中,receive將會設法將訊息與兩種模式進行匹配:
- 原子finished,表示測試結束。
- 元組{ping, Ping_PID},表示一個元組,其中有兩個單元,首先是ping原子,其次是Ping_PID繫結。
在成功匹配了某個模式之後,其中的繫結也會隨之被賦上特定的值。如匹配了{ping, Ping_PID}之後,Ping_PID便被賦值為ping這個Actor物件的識別符號。而在接下來的邏輯中,便可以使用這些“繫結”中的值。由於元組的結構不會受到任何限制,因此開發人員可以使用它來表示任意的抽象資料型別——更確切地說,應該是“資料結構”吧。
Erlang的優勢與缺陷
Erlang在訊息執行方式上的優勢在於靈活。Erlang是弱型別語言,在實現的時候可以任意調整訊息的內容,或是模式的要求。在Erlang進行模式匹配時往往有種約定:使用“原子”來表示“做什麼”,而使用“繫結”來獲取操作所需要的“資料”,這種方式避免了冗餘的cast和賦值,在使用的時候頗為靈活。然而,世上沒有完美的事物,Erlang的訊息執行方式也有缺陷,而且是較為明顯的缺陷。
首先,Erlang的資料抽象能力實在太弱。如果編寫一個略顯複雜的應用程式,您會發現程式裡充斥著複雜的元組。您可能會疲於應對那些擁有7、8個單元(甚至跟多)的元組,一個一個數過來到底某個繫結匹配的是第幾項,它的含義究竟是什麼——一旦搞錯,程式便會出錯,而且想要除錯都較為困難。因此,也有人戲稱Erlang是一門“天生會損害人視力的語言”(令人驚訝的是,那篇文章居然搜不到了,我們只能從搜尋引擎上看出點痕跡了)。
而我認為,這並不是Erlang語言中最大的問題,Erlang中最大的問題也是其“弱型別”特性。例如,現在有一個公用的Service Locator服務,任意型別的Actor都會像SL傳送一個訊息用於請求某個Service的位置,SL會在得到請求之後,向請求方傳送一條訊息表示應答。試想,如果SL的功能需要有所修改,作為回覆的訊息結構產生了變化,那麼我們勢必要修改每一個請求方中所匹配的模式。由於訊息的傳送方和接受方在實際上完全分離,沒有基於任何協議,因此靜態檢查幾乎無從做起。一旦遇到這種需要大規模的修改的情況,Erlang程式便很容易產生差錯。因為一旦有所遺漏,系統便無法正常執行下去了。
您對Erlang的感覺如何?這是一門會影響您程式設計思維的語言。老趙建議,即使您平時不會使用Erlang,也不妨簡單接觸一下這門語言。它的併發或容災等特性給了我許多啟示。相信您會有不少收穫。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-609709/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一種適合C# Actor的訊息執行方式(上):Erlang中的模式匹配C#模式
- 一種適合C# Actor的訊息執行方式(中):C# Actor的尷尬C#
- 適合C# Actor的訊息執行方式(2):C# Actor的尷尬C#
- 適合C# Actor的訊息執行方式(5):一個簡單的網路爬蟲C#爬蟲
- 適合C# Actor的訊息執行方式(3):中看不中用的解決方案C#
- Erlang Socket訊息獲取模式主動 被動 混合模式
- 【Scala篇】--Scala中Trait、模式匹配、樣例類、Actor模型AI模式模型
- Erlang中頻繁傳送遠端訊息要注意的問題
- C# 模式匹配C#模式
- Swift中的模式匹配Swift模式
- Python中執行緒的MQ訊息佇列實現以及訊息佇列的優點解析Python執行緒MQ佇列
- rabbitMQ和對應的erlang版本匹配MQ
- [elixir! #0080] 讀 erlang 開發團隊部落格 之 N 對 1 並行訊息的效能優化並行優化
- 探索AJAX中的訊息傳輸模式(一)模式
- C#中的訊息中介軟體(RabbitMQ 和 Redis)C#MQRedis
- Java多執行緒中的wait/notify通訊模式Java執行緒AI模式
- C#中的執行緒(三)多執行緒C#執行緒
- 理解TON合約中的訊息傳送結構
- 關於c#多執行緒中的幾個訊號量C#執行緒
- C# 9 新特性 —— 增強的模式匹配C#模式
- c# 多執行緒的幾種方式 【轉載】C#執行緒
- 可執行資訊和傳送訊息的服務?
- Java中的執行緒-1Java執行緒
- Java中實現執行緒的方式Java執行緒
- SQLSERVER中得到執行計劃的方式SQLServer
- C#的訊息處理方法 (轉)C#
- 多執行緒-多執行緒方式1的程式碼實現執行緒
- python 的執行緒池如何獲取 work 佇列中的訊息數量Python執行緒佇列
- flutter: 執行緒通訊與訊息迴圈Flutter執行緒
- RabbitMQ訊息模式MQ模式
- C#多執行緒程式設計(1):執行緒的啟動C#執行緒程式設計
- 一個用於實現並行執行的 Java actor 庫並行Java
- android 主執行緒和子執行緒之間的訊息傳遞Android執行緒
- Java多執行緒消費訊息Java執行緒
- python多執行緒中訊息佇列如何實現?Python執行緒佇列
- 選擇適合的型別判斷方式型別
- 為MySQL選擇合適的備份方式MySql
- InheritableThreadLocal 線上程池中進行父子執行緒間訊息傳遞出現訊息丟失的解析thread執行緒