基于消息傳遞的并發(fā)模型:Actor和CSP的區(qū)別

<blockquote>An object oriented language is a language with good support for objects.
A concurrency oriented language has good support for concurrency.

--Joe Armstrong</blockquote>

兩類通用并發(fā)模型:參考七周七并發(fā)模型

  • 共享內存型Shared Memory
    • 線程Threads
    • 鎖Locks
    • 互斥l量Mutexes
  • 消息傳送型(CSP和Actor模型)
    • 進程Processes
    • 消息Messages
    • 不共享數(shù)據(jù)(狀態(tài))No shared data

重點介紹消息傳送型的兩種模型Actor和CSP(Communicating Sequential Process)的各項對比

主要目的:除了常用的Python、Java等用的并發(fā)模型之外,還存在這么個東西

先看兩段代碼

代碼示例對比

使用Erlang代碼和Go代碼分別實現(xiàn)打印服務print_server,用來對比模型使用差異

Actor模型-Erlang代碼

%%%-------------------------------------------------------------------
%%% @author Suncle
%%% @doc
%%% print_server
%%% @end
%%% Created : 2017/12/18 14:53
%%%-------------------------------------------------------------------
-module(print_server).
-author("Flowsnow").

%% API
-export([print_server/0, start_print_server/0, send_msg/2]).


print_server() ->
  receive
    Msg ->
      io:format("print_server received msg: ~p~n", [Msg]),
      print_server()
  end.

start_print_server() ->
  Pid = spawn(?MODULE, print_server, []),
  Pid.

send_msg(Msg, Pid) ->
  Pid ! Msg,
  io:format("send_normal_msg: ~p~n", [Msg]).

Erlang shell輸出結果如下:

1> c("print_server.erl").
{ok,print_server}
2> Pid = print_server:start_print_server().
<0.39.0>
3> print_server:send_msg("hello", Pid).
send_normal_msg: "hello"
print_server received msg: "hello"
ok

以上print_server使用的是最原始的Erlang語法實現(xiàn)的,也可以使用OTP gen_server原語實現(xiàn)更加清晰易懂

CSP模型-Go代碼

print函數(shù)從channel讀取消息并阻塞,直到主函數(shù)向channel寫入hello消息

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan string)
    go print(c)
    time.Sleep(1 * time.Second)
    fmt.Println("main function: start writing msg")
    c <- "hello"

    var input string
    fmt.Scanln(&input)
}

func print(c <-chan string) {
    fmt.Println("print function: start reading")
    fmt.Println("print function: reading: " + <-c)
    time.Sleep(1 * time.Second)
}

輸出結果如下:

D:\workspace\Go>go run print_server.go
print function: start reading
main function: start writing msg
print function: reading: hello

模型圖對比

Actor

Actor1發(fā)送消息到Actor2的郵箱中,郵箱本質是隊列,由Actor2消費

CSP

Process1在Channel的寫入端添加消息,Process2在channel的讀取端讀取消息

基本特性對比

Actor

  1. 基于消息傳遞message-passing
  2. 消息和信箱機制:消息異步發(fā)送
  3. 保留可變狀態(tài)但不共享
  4. 失敗檢測和任其崩潰
  5. 重點在于發(fā)送消息時的實體

CSP

  1. 基于消息傳遞message-passing
  2. 順序進程Sequential processes
  3. 通過channel同步通信Synchronous communication through channels
  4. 頻道交替復用Multiplexing of channels with alternation
  5. 重點在于發(fā)送消息時使用的通道channel

通信語義對比

Actor

Actor1等待消息并阻塞,直到Actor2發(fā)送消息給Actor1

Actor2發(fā)送消息給Actor3,暫存在Actor3的Mailbox中,直到Actor3接受并處理

CSP

Process1讀取channel因沒有消息阻塞,直到Process2向該channel添加消息

process2向channel添加消息并阻塞,直到Process3讀取該channel消息

Erlang實現(xiàn)簡易銀行賬戶

使用Erlang原語,代碼如下:

使用OTP的gen_server,代碼如下:

Erlang小項目:IP數(shù)據(jù)庫

使用Erlang/OTP實現(xiàn)的IP數(shù)據(jù)庫,可以根據(jù)IP查詢到具體的國家省份等,代碼如下:

不一樣的Erlang特性

  1. Let it crash思想:值得借鑒

比如:執(zhí)行算術異常崩潰

  1. 變量是不可變的,變量一旦賦予值就無法再改變:帶來的好處就是沒有可變狀態(tài),就不需要內存共享,也就不需要有鎖
  2. Erlang進程之間的唯一交互方式就是消息傳遞:Erlang中沒有像C++那樣,進程間擁有多種不同的交互方式(管道、消息隊列、存儲共享等等)。

FAQ

為什么沒有容量自動增大的緩沖區(qū)?

即使現(xiàn)在有一個看上去永不枯竭的資源,總有一天這個資源還是會被用盡的。可能是因為時過境遷,當初的老程序現(xiàn)在需要解決更大規(guī)模的問題;也可能是存在一個bug,消息沒有被及時處理,導致被堆積。如果沒有思考緩沖區(qū)塞滿時的對策,那么在未來的某個時間就有可能出現(xiàn)一個破壞性極強,隱蔽性極深且難以診斷的bug。最好的策略是在現(xiàn)在就思考如何處理緩存區(qū)被塞滿的情況,將問題消滅在萌芽階段。

因此常用的緩存區(qū)類型有三種:阻塞型(blocking),棄用新值型(dropping),移出舊值型(sliding)

Python有什么消息傳遞并發(fā)模型?

Actor模型pykka:https://github.com/jodal/pykka

CSP模型pycsp:https://github.com/runefriborg/pycsp/wiki/Getting_Started_With_PyCSP


圖片均來源于here


參考:


念念不忘,必有回響,小伙伴們幫我點個贊吧,非常感謝。

我是職場亮哥,YY高級軟件工程師、四年工作經驗,拒絕咸魚爭當龍頭的斜杠程序員。

聽我說,進步多,程序人生一把梭

如果有幸能幫到你,請幫我點個【贊】,給個關注,如果能順帶評論給個鼓勵,將不勝感激。

職場亮哥文章列表:更多文章

本人所有文章、回答都與版權保護平臺有合作,著作權歸職場亮哥所有,未經授權,轉載必究!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容