Go中sync.Mutex 加锁失效的问题解决
作者:Looooking
我先声明一下,并不是真的加锁失效,而是我之前的理解有误,导致看起来像是加锁失效一样。于是乎记录一下,加深一下印象。
我之前有个理解误区(不知道大家有没有,有的话赶紧纠正一下——其实也是因为我这块的知识掌握不牢固导致的):觉得只要是加锁后,在我主动调用解锁之前,这个块范围内的变量一定不会被其他地方修改。后来验证发现,我大错特错了。
起因
最近在学习 sync.Mutex 加锁时,写了下面一段代码进行练习。
package main import ( "fmt" "sync" "time" ) type Info struct { mu sync.Mutex Value string } func Update(info *Info) { fmt.Printf("%s: before update. Value: %s\n", time.Now().Format(timeFormat), info.Value) info.mu.Lock() defer info.mu.Unlock() time.Sleep(2 * time.Second) fmt.Printf("%s: in update. Value: %s\n", time.Now().Format(timeFormat), info.Value) info.Value = "update" fmt.Printf("%s: after update. Value: %s\n", time.Now().Format(timeFormat), info.Value) } const timeFormat = "2006-01-02 15:04:05" func main() { fmt.Printf("%s: main start\n", time.Now().Format(timeFormat)) info := &Info{} var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() Update(info) }() time.Sleep(time.Second) info.Value = "main" fmt.Printf("%s: in main. Value: %s\n", time.Now().Format(timeFormat), info.Value) wg.Wait() }
按照我原先上面的理解,Update() 函数当中,before update 和 in update 中对应结构体的值应该是不会变的(毕竟我加了锁)。然而从运行结果发现,Update() 函数执行期间,结构体变量的 Value 竟然还是被外部主线程修改了。
分析
那么为什么会这样呢?明明 Update() 里边已经添加了锁,为什么执行期间还是会被其他地方修改呢?
最后发现,究其原因,还是在于主线程修改变量的值的时候,没有先判断锁 mu 是否已经释放,就直接进行了修改操作。
主线程中加上获取锁的操作后,会先判断当前锁是否被释放,如果没被释放,就会一直进行等待直到锁释放后才继续执行后面的操作。
输出结果也和预期保持一致 。
2024-04-16 23:59:13: main start
2024-04-16 23:59:13: before update. Value:
2024-04-16 23:59:15: in update. Value:
2024-04-16 23:59:15: after update. Value: update
2024-04-16 23:59:15: in main. Value: main
正常来说,锁是要配合多 goroutine 来使用的, 对于单线程来说,由于没有其他线程进行资源竞争,加锁的意义不大;对于多 goroutine 而言,对于获取和释放锁的时机,应该由应用程序合理控制。关于锁的使用,还有一些其他注意事项,这块也一并写一下。
- 在一个 goroutine 获得 Mutex 后,其他 goroutine 只能等到这个 goroutine 释放该 Mutex
- 使用 Lock() 加锁后,不能再继续对其加锁,直到利用 Unlock() 解锁后才能再加锁
- 在 Lock() 之前使用 Unlock() 会导致 panic 异常
- 已经锁定的 Mutex 并不与特定的 goroutine 相关联,这样可以利用一个 goroutine 对其加锁,再利用其他 goroutine 对其解锁
- 在同一个 goroutine 中的 Mutex 解锁之前再次进行加锁,会导致死锁
- 适用于读写不确定,并且只有一个读或者写的场景
缓冲通道实现互斥逻辑
当然,我们还可以通过缓冲为1的通道实现互斥锁的逻辑。
package main import ( "fmt" "sync" "time" ) type Info struct { Value string } func Update(info *Info, sem chan bool) { fmt.Printf("%s: before update. Value: %s\n", time.Now().Format(timeFormat), info.Value) sem <- true defer func() { <- sem }() time.Sleep(2 * time.Second) fmt.Printf("%s: in update. Value: %s\n", time.Now().Format(timeFormat), info.Value) info.Value = "update" fmt.Printf("%s: after update. Value: %s\n", time.Now().Format(timeFormat), info.Value) } const timeFormat = "2006-01-02 15:04:05" func main() { sem := make(chan bool, 1) fmt.Printf("%s: main start\n", time.Now().Format(timeFormat)) info := &Info{} var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() Update(info, sem) }() time.Sleep(time.Second) sem <- true info.Value = "main" fmt.Printf("%s: in main. Value: %s\n", time.Now().Format(timeFormat), info.Value) <- sem wg.Wait() }
2024-04-17 00:26:25: main start
2024-04-17 00:26:25: before update. Value:
2024-04-17 00:26:26: in main. Value: main
2024-04-17 00:26:27: in update. Value: main
2024-04-17 00:26:27: after update. Value: update
到此这篇关于Go中sync.Mutex 加锁失效的问题解决的文章就介绍到这了,更多相关Go sync.Mutex 加锁失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!