Golang 原子操作与互斥锁
Golang 原子操作與互斥鎖
先來看一個代碼
package mainimport ("fmt""runtime""sync" )var (counter int32wg sync.WaitGroup )func main() {wg.Add(2) // 設置需要等待的 goroutine 的數量為 2go addCounter("Gerald")go addCounter("Seligman")wg.Wait()fmt.Println("Final Counter is: %+v", counter) }func addCounter(whoAmI string) {defer wg.Done()for count := 0; count < 2; count++ {value := counter// Gosched使當前go程放棄處理器,以讓其它go程運行。它不會掛起當前go程,因此當前go程未來會恢復執行。runtime.Gosched()value++counter = value} }Final Counter is: 2
首先這個程序是起了兩個 goroutine 。每個 goroutine 都對 counter 進行了兩次累加操作,所以理論上 counter 最后應該是 4 而不是 2 。
原因是因為,Gerald 協程獲取到 counter 的值后,又讓 Seligman 協程去獲取 counter 的值。注意此時 counter 的值還沒改變,所以他們兩個協程都拿到 0 這個值。然后兩個協程又將 value++ 后賦值給 counter,等于說做了兩次 counter = 1 的操作。第二次循環也是一樣的原理。所以最后的值是 2 。
這種情況可以用兩中方式避免
- Atomic 原子操作
- Mutex 互斥鎖
Atomic
for count := 0; count < 2; count++ {value := counter// Gosched使當前go程放棄處理器,以讓其它go程運行。它不會掛起當前go程,因此當前go程未來會恢復執行。runtime.Gosched()value++counter = value}這段代碼其實有點奇怪,為什么給 counter + 1 要寫得這么多步。這其實是在模擬 CPU 內部再給你個值+1的時候需要做的操作。首先寄存器會把 counter 的值拿出來保存在寄存器里,然后在寄存器里進行+1操作,最后再把寄存器里的值放到CPU里(原理是這樣,若位置錯誤,請及時提醒,見諒)。
而 Atomic 就是相當于在 CPU 里加鎖,讓這三步操作在執行的時候,當前協程不可以被調度切換,等這三步完成之后才可以被調度切換。
package mainimport ("fmt""runtime""sync""sync/atomic" )var (counter int32wg sync.WaitGroup )func main() {wg.Add(2) // 設置需要等待的 goroutine 的數量為 2go addCounter("Gerald")go addCounter("Seligman")wg.Wait()fmt.Println("Final Counter is: ", counter) }func addCounter(whoAmI string) {defer wg.Done()for count := 0; count < 2; count++ {//value := counter// Gosched使當前go程放棄處理器,以讓其它go程運行。它不會掛起當前go程,因此當前go程未來會恢復執行。//runtime.Gosched()////value++////counter = valueatomic.AddInt32(&counter, 1)runtime.Gosched()} }Final Counter is: 4
這次 counter 就變成 4 了,符合預期。
Mutex
這個應該很熟悉了,互斥鎖,就是在對元素操作之前進行加鎖,操作完了解鎖。就可以保證數據一致了。
package mainimport ("fmt""sync" )var (counter int32wg sync.WaitGroupmutex sync.Mutex )func main() {wg.Add(2) // 設置需要等待的 goroutine 的數量為 2go addCounter("Gerald")go addCounter("Seligman")wg.Wait()fmt.Println("Final Counter is: ", counter) }func addCounter(whoAmI string) {defer wg.Done()for count := 0; count < 2; count++ {//value := counter// Gosched使當前go程放棄處理器,以讓其它go程運行。它不會掛起當前go程,因此當前go程未來會恢復執行。//runtime.Gosched()////value++////counter = value//atomic.AddInt32(&counter, 1)//runtime.Gosched()mutex.Lock()counter++mutex.Unlock()} }Final Counter is: 4
叮~🔔
總結
以上是生活随笔為你收集整理的Golang 原子操作与互斥锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: urv中保研碰撞测试结果_经撞=安全?中
- 下一篇: 思维导图相关