《Go 語言併發之道》讀後感 - 第二章

sober-wang發表於2020-09-03

Go 語言併發之道讀後感-第二章

CSP (Communicating Sequential Processes) 我們可能聽說過,通過通訊去共享記憶體,Go pipeline 等,這一切的核心思想就是 CSP 理論。在這一章作者詳盡的介紹了 CSP 與 Go 併發哲學的關係。

併發與並行的區別

併發屬於程式碼,並行屬於一個執行中的程式。

引用 Erlang 之父 Joe Armstrong 圖

併發 = 兩個佇列一個咖啡機,兩個佇列的人交替使用咖啡機,可能會遇見如下情況:

A 佇列被 B 佇列小夥伴插隊了,這就是條件競爭。

A 佇列有一位英俊小夥和 B 佇列一位女神並排,輪到他們倆的時候兩人互看對方一眼,互讓一步同時彬彬有禮的說了一句:你先。這個時候活鎖出現了。

B 佇列一位小夥伴,是代替其他朋友來打咖啡,他帶了一箱子杯子,終於輪到他了。這時飢餓就出現了。

A 佇列一位小夥伴喜歡摩卡,卡布奇諾混合咖啡,B 佇列一位小夥伴喜歡拿鐵,摩卡混合咖啡。當他們互相等待對方摩卡出杯的時候後,死鎖來了。

並行 = 兩個佇列兩臺咖啡機。

編寫正確的併發邏輯越難,越需要我們將很簡單的併發原語組合起來使用。在 Go 語言出現之前,大部分的主流變成語言都有一系列的抽象層。 如果你想寫併發程式碼,你需要對你的程式按照執行緒同步以及記憶體訪問同步來建模。如果你又一大堆需要併發建模的東西,而你的計算機又不能處理那麼多的執行緒,就需要建立一個執行緒池並將你的操作線上程池中複用。

Go 語言在這個聯調中加入了新的一環:goroutine。另外,Go 語言從著名的電腦科學家 Tony Hoare 那裡借用了不少概念,並且給我們提供了新的原語來使用,即 channel。Go 語言併發原語的根基論文: Tony Hoare 開創性的論文 “Communicating Sequential Processes”。

在 Go 語言中執行緒依舊存在,但是已經不需要我們去操心執行緒建立,複用,銷燬,合併等操作,由 Go 語言本身完成。我們只需要使用更加簡單的 goroutine 和 channel 角度。偶爾需要考慮一下共享記憶體的問題。

什麼是 CSP

當進行和 Go 語言相關的討論的時候,你經常會聽到人們丟擲 CSP 縮寫。

CSP 即 “Communicating Sequential Processes" 的縮寫,譯為通訊順序京城。在 1978 年,Richard Hoare 在國際計算機協會工作時發表的論文。

在這篇論文裡,Hoare 認為輸入與輸出時兩個被忽略的程式設計原語,尤其是在併發程式碼中。在 Hoare 寫作這篇論文的同時,物件導向反省正在成為程式設計的基石,併發操作並沒有被給與過多的思考。Hoare 開始糾正這個現象,在接下來的 6 年裡,關於 CSP 的想法被提煉成了一個叫做 "程式微積分"的正式名稱來將 CSP 的想法投入到併發程式設計實踐中。

一個程式的輸出應該直接流量另一個程式的輸入。一個由守護的命令僅僅是一個帶有左和右傾向的語句,由→來分隔。左側服務是有執行條件的,或者是守護右側服務,如果左側服務執行失敗,或者在一個命令執行後,返回 false 或者退出,右側服務永遠不會被執行。將這些與 Hoare 的思想組合機器,為 Hoare 通訊過程奠定了基礎,從而實現了 channel。

Go 如何幫助你

goroutine 把我們從必須按照並行的方式思考中解放出來,作為替代,它准許我們按照更為自然的等級對問題進行建模。

channel 幫助我們把每一個 goroutine 組合在一起。

select 語句是對 channel 的一個補充,並且是多個 channel 組合的所有難點得以實現。

這裡我推薦劉丹冰的 GMP 講解:

https://zhuanlan.zhihu.com/p/168610624

Go 語言的併發哲學

“使用同行來共享記憶體,而不是是通過共享記憶體來通訊 “ ,這句座右銘想必麼一個 Gopher 都熟知。在併發程式碼編寫中 Go 提供了併發原語 (go,channel) 和 sync 包供我們選擇,那麼我們什麼時候應該選擇併發原語,什麼時候選擇記憶體訪問同步呢?以下這副圖給出了答案:

你是否需要轉讓資料所有權?

如果需要將計算結果給你共享給其他的程式碼塊,那麼你應該選擇 channel。這樣做有兩個好處:

  1. 建立一個帶有快取的 channel 來實現一個低成本的在記憶體中的佇列來解耦你的生產者和消費者
  2. channel 確保你的併發程式碼可以和其他併發程式碼進行組合

你是否檢視在保護某個資料結構的內部狀態?

當你需要保護某一個資料結構的時候這事原子操作,臨界區進入最小狀態,我們需要將這個行為鎖起來。所以這時使用 sync 包

你是否試圖協調多個邏輯片段?

記住,channel 比記憶體同步原語更具有可組合性。Go 團隊鼓勵漫天飛的 channel ,如果滿篇鎖對於任何語言可能都是災難。

這是一個效能要求很高的臨界區嗎?

channel 使用做記憶體訪問同步來操作,因此它只能更慢。

追求簡潔,儘量使用 channel ,並且認為 goroutine 的使用是沒有成本的。

更多原創文章乾貨分享,請關注公眾號
  • 《Go 語言併發之道》讀後感 - 第二章
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章