Go語言實戰筆記(十三)| Go 並發資源競爭

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

《Go語言實戰》讀書筆記,未完待續,歡迎掃碼關注公眾號flysnow_org,第一時間看後續筆記。覺得有協助的話,順手分享到朋友圈吧,感謝支援。

有並發,就有資源競爭,如果兩個或者多個goroutine在沒有相互同步的情況下,訪問某個共用的資源,比如同時對該資源進行讀寫時,就會處於相互競爭的狀態,這就是並發中的資源競爭。

並發本身並不複雜,但是因為有了資源競爭的問題,就使得我們開發出好的並發程式變得複雜起來,因為會引起很多莫名其妙的問題。

123456789101112131415161718192021222324252627282930
package mainimport ("fmt""runtime""sync")var (count int32wg    sync.WaitGroup)func main() {wg.Add(2)go incCount()go incCount()wg.Wait()fmt.Println(count)}func incCount() {defer wg.Done()for i := 0; i < 2; i++ {value := countruntime.Gosched()value++count = value}}

這是一個資源競爭的例子,我們可以多運行幾次這個程式,會發現結果可能是2,也可以是3,也可能是4。因為共用資源count變數沒有任何同步保護,所以兩個goroutine都會對其進行讀寫,會導致對已經計算好的結果覆蓋,以至於產生錯誤結果,這裡我們示範一種可能,兩個goroutine我們暫時稱之為g1和g2。

  1. g1讀取到count為0。
  2. 然後g1暫停了,切換到g2運行,g2讀取到count也為0。
  3. g2暫停,切換到g1,g1對count+1,count變為1。
  4. g1暫停,切換到g2,g2剛剛已經擷取到值0,對其+1,最後賦值給count還是1
  5. 有沒有注意到,剛剛g1對count+1的結果被g2給覆蓋了,兩個goroutine都+1還是1

不再繼續示範下去了,到這裡結果已經錯了,兩個goroutine相互覆蓋結果。我們這裡的runtime.Gosched()是讓當前goroutine暫停意思,退回執行隊列,讓其他等待的goroutine運行,目的是讓我們示範資源競爭的結果更明顯。注意,這裡還會牽涉到CPU問題,多核會並行,那麼資源競爭的效果更明顯。

所以我們對於同一個資源的讀寫必須是原子化的,也就是說,同一時間只能有一個goroutine對共用資源進行讀寫操作

共用資源競爭的問題,非常複雜,並且難以察覺,好在Go為我們提供了一個工具協助我們檢查,這個就是go build -race命令。我們在當前項目目錄下執行這個命令,產生一個可以執行檔案,然後再運行這個可執行檔,就可以看到列印出的檢測資訊。

1
go build -race

多加了一個-race標誌,這樣產生的可執行程式就內建了檢測資源競爭的功能,下面我們運行,也是在終端運行。

1
./hello

我這裡樣本產生的可執行檔名是hello,所以是這麼啟動並執行,這時候,我們看終端輸出的檢測結果。

123456789101112131415161718192021
➜  hello ./hello       ==================WARNING: DATA RACERead at 0x0000011a5118 by goroutine 7:  main.incCount()      /Users/xxx/code/go/src/flysnow.org/hello/main.go:25 +0x76Previous write at 0x0000011a5118 by goroutine 6:  main.incCount()      /Users/xxx/code/go/src/flysnow.org/hello/main.go:28 +0x9aGoroutine 7 (running) created at:  main.main()      /Users/xxx/code/go/src/flysnow.org/hello/main.go:17 +0x77Goroutine 6 (finished) created at:  main.main()      /Users/xxx/code/go/src/flysnow.org/hello/main.go:16 +0x5f==================4Found 1 data race(s)

看,找到一個資源競爭,連在那一行代碼出了問題,都標示出來了。goroutine 7在代碼25行讀取共用資源value := count,而這時goroutine 6正在代碼28行修改共用資源count = value,而這兩個goroutine都是從main函數啟動的,在16、17行,通過go關鍵字。

既然我們已經知道共用資源競爭的問題,是因為同時有兩個或者多個goroutine對其進行了讀寫,那麼我們只要保證,同時只有一個goroutine讀寫不就可以了,現在我們就看下傳統解決資源競爭的辦法–對資源加鎖。

Go語言提供了atomic包和sync包裡的一些函數對共用資源同步枷鎖,我們先看下atomic包。

12345678910111213141516171819202122232425262728293031
package mainimport ("fmt""runtime""sync""sync/atomic")var (count int32wg    sync.WaitGroup)func main() {wg.Add(2)go incCount()go incCount()wg.Wait()fmt.Println(count)}func incCount() {defer wg.Done()for i := 0; i < 2; i++ {value := atomic.LoadInt32(&count)runtime.Gosched()value++atomic.StoreInt32(&count,value)}}

留意這裡atomic.LoadInt32atomic.StoreInt32兩個函數,一個讀取int32類型變數的值,一個是修改int32類型變數的值,這兩個都是原子性的操作,Go已經協助我們在底層使用加鎖機制,保證了共用資源的同步和安全,所以我們可以得到正確的結果,這時候我們再使用資源競爭偵查工具go build -race檢查,也不會提示有問題了。

atomic包裡還有很多原子化的函數可以保證並發下資源同步訪問修改的問題,比如函數atomic.AddInt32可以直接對一個int32類型的變數進行修改,在原值的基礎上再增加多少的功能,也是原子性的,這裡不再舉例,大家自己可以試試。

atomic雖然可以解決資源競爭問題,但是比較都是比較簡單的,支援的資料類型也有限,所以Go語言還提供了一個sync包,這個sync包裡提供了一種互斥型的鎖,可以讓我們自己靈活的控制哪些代碼,同時只能有一個goroutine訪問,被sync互斥鎖控制的這段代碼範圍,被稱之為臨界區,臨界區的代碼,同一時間,只能又一個goroutine訪問。剛剛那個例子,我們還可以這麼改造。

123456789101112131415161718192021222324252627282930313233
package mainimport ("fmt""runtime""sync")var (count int32wg    sync.WaitGroupmutex sync.Mutex)func main() {wg.Add(2)go incCount()go incCount()wg.Wait()fmt.Println(count)}func incCount() {defer wg.Done()for i := 0; i < 2; i++ {mutex.Lock()value := countruntime.Gosched()value++count = valuemutex.Unlock()}}

執行個體中,新聲明了一個互斥鎖mutex sync.Mutex,這個互斥鎖有兩個方法,一個是mutex.Lock(),一個是mutex.Unlock(),這兩個之間的地區就是臨界區,臨界區的代碼是安全的。

樣本中我們先調用mutex.Lock()對有競爭資源的代碼加鎖,這樣當一個goroutine進入這個地區的時候,其他goroutine就進不來了,只能等待,一直到調用mutex.Unlock() 釋放這個鎖為止。

這種方式比較靈活,可以讓代碼編寫者任意定義需要保護的代碼範圍,也就是臨界區。除了原子函數和互斥鎖,Go還為我們提供了更容易在多個goroutine同步的功能,這就是通道chan,我們下篇講。

《Go語言實戰》讀書筆記,未完待續,歡迎掃碼關注公眾號flysnow_org,第一時間看後續筆記。覺得有協助的話,順手分享到朋友圈吧,感謝支援。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.