Go并發(fā)編程中鎖機制的常見錯誤:為什么1000個協(xié)程累加結(jié)果并非1000?

Go并發(fā)編程中鎖機制的常見錯誤:為什么1000個協(xié)程累加結(jié)果并非1000?

Go并發(fā)編程中的sync.Mutex鎖及常見錯誤分析

本文剖析一段使用sync.Mutex鎖和sync.WaitGroup進行并發(fā)編程的Go代碼,這段代碼試圖通過1000個協(xié)程累加一個變量,但最終結(jié)果與預(yù)期(1000)不一致。讓我們來分析代碼并找出問題所在。

示例代碼:

package main  import (     "fmt"     "sync"     "time" )  func main() {     haslockandwait() }  func haslockandwait() {     var a = 0     var wg sync.WaitGroup      for i := 0; i < 1000; i++ {         wg.Add(1)         go func(i int) {             defer wg.Done()             var locker sync.Mutex // 錯誤:鎖聲明在此處             locker.Lock()             a++             locker.Unlock()         }(i)     }     wg.Wait()     fmt.Println("最終結(jié)果:", a) }

代碼預(yù)期結(jié)果是a最終值為1000,但實際運行結(jié)果往往小于1000。這是因為sync.Mutex的聲明位置錯誤。

問題根源:var locker sync.Mutex這行代碼在每個goroutine內(nèi)部聲明,這意味著每個goroutine都創(chuàng)建了一個獨立的sync.Mutex實例。這些鎖互不干擾,多個goroutine可以同時修改a,導(dǎo)致結(jié)果不準確。

解決方案:將sync.Mutex的聲明移到for循環(huán)之外,使其成為全局鎖,確保所有g(shù)oroutine使用同一個鎖來保護a變量。修改后的代碼如下:

func hasLockAndWait() {     var a = 0     var wg sync.WaitGroup     var locker sync.Mutex // 正確:鎖聲明在此處      for i := 0; i < 1000; i++ {         wg.Add(1)         go func(i int) {             defer wg.Done()             locker.Lock()             a++             locker.Unlock()         }(i)     }     wg.Wait()     fmt.Println("最終結(jié)果:", a) }

另一種更簡潔高效的解決方案是使用atomic.AddInt64函數(shù),它提供原子操作,無需鎖即可保證線程安全:

import "sync/atomic"  func atomicadd() {     var a int64 = 0     var wg sync.WaitGroup      for i := 0; i < 1000; i++ {         wg.Add(1)         go func() {             defer wg.Done()             atomic.AddInt64(&a, 1)         }()     }     wg.Wait()     fmt.Println("最終結(jié)果:", a) }

通過以上修改,可以確保并發(fā)累加操作的正確性,最終結(jié)果將為1000。 這強調(diào)了在Go并發(fā)編程中正確使用鎖機制的重要性,錯誤的鎖使用會導(dǎo)致數(shù)據(jù)競爭和不正確的程序行為。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊11 分享