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)聲明
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載。
THE END