Go 中 sync.Mutex 锁失效之谜:为什么在并发访问共享变量时,使用 sync.Mutex 并不能保证结果正确?

go 中 sync.mutex 锁失效之谜:为什么在并发访问共享变量时,使用 sync.mutex 并不能保证结果正确?

sync.mutex 锁失效之谜

在探索 go 中并发的 sync.mutex 时,一位新手遇到了令人困惑的问题。他们编写了一个程序,目标是使用 1000 个协程对一个变量加 1000,每个协程 +1,并期待最终结果为 1000。

然而,代码执行后,却得到了随机的结果,让新手心灰意冷。问题出在哪里?

问题代码分析

var a = 0
var wg sync.waitgroup

for i := 0; i < 1000; i++ {
    wg.add(1)

    go func() {
        defer wg.done()
        var locker sync.mutex
        locker.lock()
        defer locker.unlock()
        a++
        fmt.println("a 的值为:", a)
    }()
}

新手使用的 sync.mutex 旨在控制对共享变量 a 的访问,确保同一时刻只有一个协程操作它。但是,问题在于 locker.lock() 和 locker.unlock() 被放置在匿名函数内部,而不是for循环中。

解决方案

为了解决这个问题,只需将 locker.lock() 和 locker.unlock() 移动到 for 循环中。这样,每个协程都会使用自己的 locker 对象,从而实现正确的锁定机制。

var locker sync.mutex

for i := 0; i < 1000; i++ {
    wg.add(1)

    go func() {
        defer wg.done()
        locker.lock()
        defer locker.unlock()
        a++
        fmt.println("a 的值为:", a)
    }()
}

另一种选择是使用 atomic.addint64() 函数,它提供了一种线程安全的原子操作,可以直接对变量进行加减。

func hasLockAndWait() {
    var a int64 // 使用 int64 以匹配 atomic.AddInt64()

    for i := 0; i < 1000; i++ {
        wg.Add(1)

        go func() {
            defer wg.Done()
            atomic.AddInt64(&a, 1)
            fmt.Println("a 的值为:", a)
        }()
    }

    wg.Wait()
    fmt.Println("a 的最终值为:", a)
}

通过采用这些解决方案,新手应该能够获得预期的结果:变量 a 的最终值为 1000。

以上就是Go 中 sync.Mutex 锁失效之谜:为什么在并发访问共享变量时,使用 sync.Mutex 并不能保证结果正确?的详细内容,更多请关注其它相关文章!