An object oriented language is a language with good support for objects.
A concurrency oriented language has good support for concurrency.
--Joe Armstrong
兩類通用并發模型:參考七周七并發模型
-
共享記憶體型Shared Memory
- 執行緒Threads
- 鎖Locks
- 互斥l量Mutexes
-
訊息傳送型(CSP和Actor模型)
- 行程Processes
- 訊息Messages
- 不共享資料(狀態)No shared data
重點介紹訊息傳送型的兩種模型Actor和CSP(Communicating Sequential Process)的各項對比
主要目的:除了常用的Python、Java等用的并發模型之外,還存在這么個東西
先看兩段代碼
代碼示例對比
使用Erlang代碼和Go代碼分別實作列印服務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語法實作的,也可以使用OTP gen_server原語實作更加清晰易懂
CSP模型-Go代碼
print函式從channel讀取訊息并阻塞,直到主函式向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發送訊息到Actor2的郵箱中,郵箱本質是佇列,由Actor2消費
CSP

Process1在Channel的寫入端添加訊息,Process2在channel的讀取端讀取訊息
基本特性對比
Actor
- 基于訊息傳遞message-passing
- 訊息和信箱機制:訊息異步發送
- 保留可變狀態但不共享
- 失敗檢測和任其崩潰
- 重點在于發送訊息時的物體
CSP
- 基于訊息傳遞message-passing
- 順序行程Sequential processes
- 通過channel同步通信Synchronous communication through channels
- 頻道交替復用Multiplexing of channels with alternation
- 重點在于發送訊息時使用的通道channel
通信語意對比
Actor

Actor1等待訊息并阻塞,直到Actor2發送訊息給Actor1
Actor2發送訊息給Actor3,暫存在Actor3的Mailbox中,直到Actor3接受并處理
CSP

Process1讀取channel因沒有訊息阻塞,直到Process2向該channel添加訊息
process2向channel添加訊息并阻塞,直到Process3讀取該channel訊息
Erlang實作簡易銀行賬戶
使用Erlang原語,代碼如下:
- https://gist.github.com/Flowsnow/5da4565718bb6c3ec3f0a79cfedf0b00
使用OTP的gen_server,代碼如下:
- https://gist.github.com/Flowsnow/18a580313ac0b7ea54e5eddd9e2b2265
Erlang小專案:IP資料庫
使用Erlang/OTP實作的IP資料庫,可以根據IP查詢到具體的國家省份等,代碼如下:
- https://github.com/Flowsnow/ip_db
不一樣的Erlang特性
- Let it crash思想:值得借鑒
- https://www.zhihu.com/question/21325941/answer/173370966
比如:執行算術例外崩潰
- 變數是不可變的,變數一旦賦予值就無法再改變:帶來的好處就是沒有可變狀態,就不需要記憶體共享,也就不需要有鎖
- Erlang行程之間的唯一互動方式就是訊息傳遞:Erlang中沒有像C++那樣,行程間擁有多種不同的互動方式(管道、訊息佇列、存盤共享等等),
FAQ
為什么沒有容量自動增大的緩沖區?
即使現在有一個看上去永不枯竭的資源,總有一天這個資源還是會被用盡的,可能是因為時過境遷,當初的老程式現在需要解決更大規模的問題;也可能是存在一個bug,訊息沒有被及時處理,導致被堆積,如果沒有思考緩沖區塞滿時的對策,那么在未來的某個時間就有可能出現一個破壞性極強,隱蔽性極深且難以診斷的bug,最好的策略是在現在就思考如何處理快取區被塞滿的情況,將問題消滅在萌芽階段,
因此常用的快取區型別有三種:阻塞型(blocking),棄用新值型(dropping),移出舊值型(sliding)
Python有什么訊息傳遞并發模型?
Actor模型pykka:https://github.com/jodal/pykka
CSP模型pycsp:https://github.com/runefriborg/pycsp/wiki/Getting_Started_With_PyCSP
圖片均來源于here!
參考:
- Communicating Sequential Processes (CSP)-An alternative to the actor model
- Concurrency Oriented Programming In Erlang-Joe Armstrong.pdf
記得幫我點贊哦!
精心整理了計算機各個方向的從入門、進階、實戰的視頻課程和電子書,按照目錄合理分類,總能找到你需要的學習資料,還在等什么?快去關注下載吧!!!

念念不忘,必有回響,小伙伴們幫我點個贊吧,非常感謝,
我是職場亮哥,YY高級軟體工程師、四年作業經驗,拒絕咸魚爭當龍頭的斜杠程式員,
聽我說,進步多,程式人生一把梭
如果有幸能幫到你,請幫我點個【贊】,給個關注,如果能順帶評論給個鼓勵,將不勝感激,
職場亮哥文章串列:更多文章

本人所有文章、回答都與著作權保護平臺有合作,著作權歸職場亮哥所有,未經授權,轉載必究!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/236891.html
標籤:Go
上一篇:【Delphi】Utils.Generics.ADODB
下一篇:Goproxy.cn 核心代碼探究;https://github.com/goproxy/goproxy 開源模塊分析;
