點擊上方藍字進行關注
前文傳送門:《三分鐘掌握共享內存模型和 Actor模型》, 一直想比較Actor模型與golang的CSP模型,經過一段時間的實戰記錄了本文。
Actor?vs?CSP模型
??傳統多線程的的共享內存(ShareMemory)模型使用lock,condition等同步原語來強行規定進程的執行順序。
??Actor模型,是基于消息傳遞的并發模型,? ?強調的是Actor這個工作實體,每個Actor自行決定消息傳遞的方向(要傳遞的ActorB),通過消息傳遞形成流水線。

本文現在要記錄的是另一種基于消息傳遞的并發模型:CSP(communicating sequential process順序通信過程)。
在CSP模型,worker之間不直接彼此聯系,強調信道在消息傳遞中的作用,不謀求形成流水線。
消息的發送者和接受者通過該信道松耦合,發送者不知道自己消息被哪個接受者消費了,接受者也不知道是從哪個發送者發送的消息。

go的信道
go的信道[1]是golang協程同步和通信的原生方式。
同map,slice一樣,channel通過make內置函數初始化并返回引用,引用可認為是常量指針[2]。
兩種信道:
1. 無緩沖區信道:讀寫兩端就緒后,才能通信(一方沒就緒就阻塞)
這種方式可以用來在goroutine中進行同步,而不必顯式鎖或者條件變量。
2. 有緩沖區信道:就有可能不阻塞, 只有buffer滿了,寫入才會阻塞;只有buffer空了,讀才會阻塞。
go的信道暫時先聊到這里。
我們來用以上背景做一道 有意思的面試題吧 。
調試多線程的都懂.gif
兩個線程輪流打印0到100?
我不會啥算法,思路比較弱智:#兩線程#, #打印奇/偶數#, 我先復刻這兩個標簽。
通過go的無緩沖信道的同步阻塞的能力對齊每一次循環。
package?mainimport?("fmt""strconv""sync"
)var?wg?sync.WaitGroup
var?ch1?=?make(chan?struct{})func?main()?{wg.Add(2)go?func()?{defer?wg.Done()for?i?:=?0;?i?<=?100;?i++?{ch1?<-?struct{}{}if?i%2?==?0?{?//?偶數fmt.Println("g0??"?+?strconv.Itoa(i))}}}()go?func()?{defer?wg.Done()for?i?:=?0;?i?<=?100;?i++?{<-ch1if?i%2?==?1?{?//?奇數fmt.Println("g1?"?+?strconv.Itoa(i))}}}()wg.Wait()
}
題解:兩個協程都執行0到100次循環,但是不管哪個線程跑的快,在每次循環輸出時均會同步對齊, 每次循環時只輸出一個奇/偶值, 這樣也不用考慮兩個協程的啟動順序。

思考我的老牌勁語C#要完成本題要怎么做?
依舊是#兩線程#、#打印奇偶數#, 我沒找到C#中能多次對齊線程的能力, 于是使用兩線程相互通知的方式。
volatile?static?int?i?=?0;static?AutoResetEvent?are?=?new?AutoResetEvent(true);
static?AutoResetEvent?are2?=?new?AutoResetEvent(false);
public?static?void?Main(String[]?args)
{Thread?thread1?=?new?Thread(()?=>{for?(var?i=0;i<=100;i++){are.WaitOne();if?(i?%?2?==?0){Console.WriteLine(i?+?"==?偶數");}are2.Set();}});Thread?thread2?=?new?Thread(()?=>{for?(var?i?=?0;?i?<=?100;?i++){are2.WaitOne();if?(i?%?2?==?1){Console.WriteLine(i?+?"==?奇數");}are.Set();}
});thread1.Start();thread2.Start();Console.ReadKey();
}
注意:
? volatile:提醒編譯器或運行時不對字段做優化(處于性能,編譯器/runtime會對同時執行的線程訪問的同一字段進行優化,加volatile忽略這種優化?)。
??Object-->MarshalByRefObject-->WaitHandle-->EventWaitHandle--->AutoResetEvent[3]?本次使用了2個自動重置事件來切換通知,由一個線程通知另外一個線程執行。
(這個思路是群內各大佬討論的結果, @yi念之間? @AutoCSer,? 歡迎童鞋們提供更多方法。)
引用鏈接
[1]
?go的信道:?https://www.runoob.com/w3cnote/go-channel-intro.html[2]
?常量指針:?https://zhuanlan.zhihu.com/p/133225100[3]
?AutoResetEvent:?https://docs.microsoft.com/en-us/dotnet/api/system.threading.autoresetevent?view=net-6.0
本文內容和制圖均為原創,文章永久更新地址請參閱左下角原文,如對您有所幫助,請不吝分享?。
專題相關:一網打盡
年終總結:2021技術文大盤點 ?| ?打包過去,面向未來
項目總結:麻雀雖小,五臟俱全
理念總結:實話實說:只會.NET,會讓我們一直處于鄙視鏈、食物鏈的下游
云原生系列:?什么是云原生?
掃碼關注我們
不會讓您失望的。