週末又到了,爲你們準備了一份實用乾貨:如何使用channel和Mutex解決併發問題,利用週末的好時光,配上音樂,思考一下吧🤔。html
來,問本身個問題:面對併發問題,是用channel解決,仍是用Mutex解決?git
若是本身內心尚未清晰的答案,那就讀下這篇文章,你會了解到:github
前面不少篇的文章都在圍繞channel
介紹,而只有前一篇sync
的文章介紹到了Mutex
,不是我偏愛,而是channel在Golang是first class級別的,設計在語言特性中的,而Mutex
只是一個包中的。這就註定了一個是主角,一個是配角。golang
而且Golang還有一個併發座右銘,在《Effective Go》的channel介紹中寫到:緩存
Share memory by communicating, don't communicate by sharing memory.
經過通訊共享內存,而不是經過共享內存而通訊。
Golang以如此明顯的方式告訴咱們:面對併發問題,你首先想到的應該是channel,由於channel是線程安全的而且不會有數據衝突,比鎖好用多了。安全
既生瑜,何生亮。既然有channel
了,爲啥還提供sync.Mutex
呢?併發
主角不是萬能的,他也須要配角。在Golang裏,channel也不是萬能的,這是由channel的特性和侷限形成的。less
下面就給你們介紹channel的特色、核心方法和缺點。異步
channel的核心是數據流動,關注到併發問題中的數據流動,把流動的數據放到channel中,就能使用channel解決這個併發問題。這個思路是從Go語言的核心開發者的演講中學來的,然而視頻我已經找不到了,否則直接共享給你們,他提到了Golang併發的核心實踐的4個點:函數
DataFlow -> Drawing -> Pipieline -> Exiting
DataFlow指數據流動,Drawing指把數據流動畫出來,Pipeline指的是流水線,Exit指協程的退出。DataFlow + Drawing就是我提到到channel解決併發問題的思路,Pipeline和Exit是具體的實踐模式,Pipeline和Exit我都寫過文章,有須要自取:
下面我使用例子具體解釋DataFlow + Drawing。借用《Golang併發的次優選擇:sync包》中銀行的例子,介紹如何使用channel解決例子中銀行的併發問題:銀行支持多個用戶的同時操做。順便看下同一個併發問題,使用channel和Mutex解決是什麼差異。
一塊兒分析下多個用戶同時操做銀行的數據流動:
你必定發現了上面的數據流動:
channel是數據流動的通道/管道,爲流動的數據創建通道,這裏須要創建2類channel:
咱們把channel添加到上圖中,獲得下面的圖:
以上就是從數據流動的角度,發現如何使用channel解決併發問題。思路有了,再思考下代碼層面須要怎麼作:
銀行:
以上,咱們這個併發問題的邏輯實現和各塊工做就清晰了,寫起來也方便、簡單。代碼實現有200多行,公衆號不方便查看,能夠點閱讀原文,一鍵直達。
代碼不能貼了,運行結果仍是能夠的,爲了方便理解結果,介紹下示例代碼作了什麼。main函數建立了銀行、小明、小剛3個併發協程:
reqCh
接收請求,依次處理每一個請求,直到通道關閉,把請求交給處理函數,處理函數把結果寫入到請求中的retCh
。reqCh
。main函數最後使用WaitGroup等待小明、小剛結束後退出。
下面是運行結果:
$ go run channel_map.go xiaogang deposite 100 success xiaoming deposite 100 success xiaogang withdraw 200 failed xiaoming withdraw 20 success xiaogang has 100 xiaoming has 80 Bank exit
這一遭搞完,發現啥沒有?用Mutex直接加鎖、解鎖完事了,但channel搞出來一坨,是否是用channel解決這個問題不太適合?是的。對於當前這個問題,和Mutex的方案相比,channel的方案顯的有點「重」,不夠簡潔、高效、易用。
但這個例子展現了3點:
如今,回到了開篇的問題:同一個併發問題,你是用channel解決,仍是用mutex解決?下面,一塊兒看看怎麼選擇。
面對一個併發問題的時候,應當選擇合適的併發方式:channel仍是mutex。選擇的依據是他們的能力/特性:channel的能力是讓數據流動起來,擅長的是數據流動的場景,《Channel or Mutex》中給了3個數據流動的場景:
mutex的能力是數據不動,某段時間只給一個協程訪問數據的權限擅長數據位置固定的場景,《Channel or Mutex》中給了2個數據不動場景:
map
就是一種狀態提供解決併發問題的一個思路:
面對併發問題,除了channel or mutex,你還有另一個選擇:channel plus mutex。
一個大併發問題,能夠分解成不少小的併發問題,每一個小的併發均可以單獨選型:channel or mutex。但對於整個大的問題,一般不是channel or mutex,而是channel plus mutex。
若是你是認爲是channel and mutex也行,但我更喜歡plus,體現相互配合。
讀到這裏,感受這篇文章頭重腳輕,channel的講了不少,而channel和mutex的選擇卻講的不多。在channel和mutex的選擇,實際並無一個固定答案,也沒有固定的方法,但提供了一個簡單的思路:設計出channel和Mutex的簡單方案,而後選擇最適合當前業務、問題的那個。
思考比結論更重要,但願你有所收穫:
- 若是這篇文章對你有幫助,請點個贊/喜歡,感謝。
- 本文做者:大彬
- 若是喜歡本文,隨意轉載,但請保留此原文連接:http://lessisbetter.site/2019/01/14/golang-channel-and-mutex/