Go 語言入門很簡單 -- 16. Go 并發互斥鎖 #私藏項目實操分享#

      網友投稿 803 2025-03-31

      互斥是并發編程中最關鍵的概念之一。當我們使用 goruntine 和channels 進行并發編程時,如果兩個?goruntine 嘗試同時訪問同一個內存位置的同一數據會發生競爭,有時候會產生意想不到的結果,通常很難調試,不符合日常要求,出現錯誤甚至很難修復。


      生活場景

      假設在生活中可能會發生的例子:有一個銀行系統,我們可以從銀行余額中存款和取款。在一個單線程的同步程序中,這個操作很簡單。我們可以通過少量的單元測試有效地保證它每次都能按計劃工作。

      然而,如果我們開始引入多個線程,在 Go?語言中使用多個 goroutine,我們可能會開始在我們的代碼中看到問題。

      假如有一個余額為 1000 元的客戶。

      Go 語言入門很簡單 -- 16. Go 并發互斥鎖 #私藏項目實操分享#

      客戶將 500 元存入他的賬戶。

      一個 goroutine 會看到這個交易,讀取價值為 1000 ,并繼續將 500 添加到現有的余額中。(此時應該是 1500 的余額)

      然而,在同一時刻,他拿 800 元來還分期付款的 iphone 13.

      第二個程序在第一個程序能夠增加 500 元的額外存款之前,讀取了 1000 元的賬戶余額,并繼續從他的賬戶中扣除 800 元。(1000 - 800 = 200)

      第二天,客戶檢查了他的銀行余額,發現他的賬戶余額減少到了 200 元,因為第二個程序沒有意識到第一筆存款,并在存款完成前做了扣除操作。

      這就是一個線程競賽的例子,如果我們不小心落入這樣的代碼,我們的并發程序就會出現問題。

      互斥鎖和讀寫鎖

      互斥鎖,英文名 Mutex,顧名思義,就是相互排斥,是保護程序中臨界區的一種方式。

      而臨界區是程序中需要獨占訪問共享資源的區域。互斥鎖提供了一種安全的方式來表示對這些共享資源的獨占訪問。

      為了使用資源,channel 通過通信共享內存,而 Mutex 通過開發人員的約定同步訪問共享內存

      讓我們看一個沒有 Mutex 的并發編程示例

      package mainimport ( "fmt" "sync")type calculation struct { sum int}func main() { test := calculation{} test.sum = 0 wg := sync.WaitGroup{} for i := 0; i < 500; i++ { wg.Add(1) go dosomething(&test, &wg) } wg.Wait() fmt.Println(test.sum)}func dosomething(test *calculation, wg *sync.WaitGroup) { test.sum++ wg.Done()}

      第一次結果為:491

      第二次結果:493

      [Running] go run "e:\Coding Workspaces\LearningGoTheEasiestWay\concurrency\mutex\v0\main.go"493

      在上面的例子中,我們聲明了一個名為 test 的計算結構體,并通過 for 循環產生了多個 GoRoutines,將 sum 的值加 1。(如果你對 GoRoutines 和 WaitGroup 不熟悉,請參考之前的教程)。 我們可能期望 for 循環后 sum 的值應該是 500。然而,這可能不是真的。 有時,您可能會得到小于 500(當然永遠不會超過 500)的結果。 這背后的原因是兩個 GoRoutine 有一定的概率在相同的內存位置操作相同的變量,從而導致這種意外結果。 這個問題的解決方案是使用互斥鎖。

      使用 Mutex

      package mainimport ( "fmt" "sync")type calculation struct { sum int mutex sync.Mutex}func main() { test := calculation{} test.sum = 0 wg := sync.WaitGroup{} for i := 0; i < 500; i++ { wg.Add(1) go dosomething(&test, &wg) } wg.Wait() fmt.Println(test.sum)}func dosomething(test *calculation, wg *sync.WaitGroup) { test.mutex.Lock() test.sum++ test.mutex.Unlock() wg.Done()}

      結果為:

      [Running] go run "e:\Coding Workspaces\LearningGoTheEasiestWay\concurrency\mutex\v0.1\main.go"500

      在第二個示例中,我們在結構中添加了一個互斥鎖屬性,它是一種類型的 sync.Mutex。然后我們使用互斥鎖的 Lock() 和 Unlock() 來保護 test.sum 當它被并發修改時,即 test.sum++。

      請記住,使用互斥鎖并非沒有后果,因為它會影響應用程序的性能,因此我們需要適當有效地使用它。 如果你的 GoRoutines 只讀取共享數據而不寫入相同的數據,那么競爭條件就不會成為問題。 在這種情況下,您可以使用 RWMutex 代替 Mutex 來提高性能時間。

      Defer 關鍵字

      對 Unlock() 使用 defer 關鍵字通常是一個好習慣。

      func dosomething(test *calculation) error{ test.mutex.Lock() defer test.mutex.Unlock() err1 :=... if err1 != nil { return err1 } err2 :=... if err2 != nil { return err2 } // ... do more stuff ... return nil}

      在這種情況下,我們有多個 if err!=nil 這可能會導致函數提前退出。 通過使用 defer,無論函數如何返回,我們都可以保證釋放鎖。 否則,我們需要將 Unlock() 放在函數可能返回的每個地方。 然而,這并不意味著我們應該一直使用 defer。 讓我們再看一個例子。

      func dosomething(test *calculation){ test.mutex.Lock() defer test.mutex.Unlock() // modify the variable which requires mutex protect test.sum =... // perform a time consuming IO operation http.Get()}

      在這個例子中,mutex 不會釋放鎖,直到耗時的函數(這里是 http.Get())完成。 在這種情況下,我們可以在 test.sum=... 行之后解鎖互斥鎖,因為這是我們操作變量的唯一地方。

      總結

      很多時候 Mutex 并不是單獨使用的,而是嵌套在 Struct 中使用,作為結構體的一部分,如果嵌入的 struct 有多個字段,我們一般會把 Mutex 放在要控制的字段上面,然后使用空格把字段分隔開來。

      甚至可以把獲取鎖、釋放鎖、計數加一的邏輯封裝成一個方法。

      Go

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:wps中10的4次方怎么打(10的四次方怎么打出來wps)
      下一篇:帶你走進“華為鏈”,一個基于華為全新自研內核的區塊鏈平臺
      相關文章
      亚洲v国产v天堂a无码久久| 亚洲精品岛国片在线观看| 亚洲av永久无码精品国产精品| 亚洲色欲久久久久综合网| 男人的天堂亚洲一区二区三区 | 日韩国产欧美亚洲v片| 亚洲欧洲无卡二区视頻| 亚洲日韩一区精品射精| 亚洲经典千人经典日产| 国产精品亚洲专区无码唯爱网| 久久精品国产亚洲AV未满十八| 亚洲av无码一区二区三区天堂| 婷婷亚洲综合一区二区| 免费亚洲视频在线观看| 亚洲精品成a人在线观看| 久久激情亚洲精品无码?V| 国产成人综合亚洲AV第一页 | 亚洲a视频在线观看| va天堂va亚洲va影视中文字幕| 激情综合亚洲色婷婷五月| 亚洲无吗在线视频| 亚洲熟妇无码八V在线播放| 精品亚洲国产成人av| 亚洲国产成人久久笫一页| 超清首页国产亚洲丝袜| 亚洲精品无码永久中文字幕| 久久精品亚洲综合一品| 久久亚洲精品成人AV| 亚洲人和日本人jizz| 亚洲综合无码一区二区痴汉| 在线观看亚洲视频| 久久精品国产亚洲Aⅴ香蕉| 久久九九亚洲精品| 亚洲色偷偷偷网站色偷一区| 亚洲制服丝袜中文字幕| 亚洲变态另类一区二区三区| 亚洲国产精品尤物YW在线观看| 在线精品亚洲一区二区三区| 久久亚洲国产伦理| 亚洲成a人片在线观看播放| 亚洲字幕AV一区二区三区四区 |