
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并发编程中正确使用锁机制的重要性,错误的锁使用会导致数据竞争和不正确的程序行为。
以上就是Go并发编程中锁机制的常见错误:为什么1000个协程累加结果并非1000?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号