Go并发编程中的sync.Mutex锁及常见错误分析
本文剖析一段使用sync.Mutex锁和sync.WaitGroup进行并发编程的Go代码,这段代码试图通过1000个协程累加一个变量,但最终结果与预期(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("最终结果:", a) }
代码预期结果是a最终值为1000,但实际运行结果往往小于1000。这是因为sync.Mutex的声明位置错误。
问题根源:var locker sync.Mutex这行代码在每个goroutine内部声明,这意味着每个goroutine都创建了一个独立的sync.Mutex实例。这些锁互不干扰,多个goroutine可以同时修改a,导致结果不准确。
解决方案:将sync.Mutex的声明移到for循环之外,使其成为全局锁,确保所有goroutine使用同一个锁来保护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("最终结果:", a) }
另一种更简洁高效的解决方案是使用atomic.AddInt64函数,它提供原子操作,无需锁即可保证线程安全:
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("最终结果:", a) }
通过以上修改,可以确保并发累加操作的正确性,最终结果将为1000。 这强调了在Go并发编程中正确使用锁机制的重要性,错误的锁使用会导致数据竞争和不正确的程序行为。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END