Go语言Mutex未锁定问题求助:非指针接收者函数可访问受保护结构体
sync.Mutex未阻止无锁操作的原因
我在使用sync.Mutex时发现了异常行为,因此编写了如下测试代码:
package main import ( "fmt" "sync" ) type Box struct { Stuff int mu sync.Mutex } var box Box func init() { box = Box{} box.mu.Lock() } func (box *Box) add() { fmt.Println("add()") box.mu.Unlock() box.Stuff++ box.mu.Lock() } func (box *Box) display() { fmt.Print("display(): ") box.mu.Unlock() fmt.Println(box.Stuff) box.mu.Lock() } func add2() { fmt.Println("add2()") box.Stuff++ //increments but should not, error expected } func display2() { fmt.Print("display2(): ") fmt.Println(box.Stuff) //displays but should not, error expected } func main() { box.display() box.add() box.display() display2() add2() box.display() }
运行代码后得到如下输出:
display(): 0 add() display(): 1 display2(): 1 add2() display(): 2
我原本预期名称后缀为2的两个函数(add2()和display2())会执行失败,但实际并未出现报错。请问这是什么原因?
问题解析
1. sync.Mutex的本质特性
Go的sync.Mutex是被动的互斥工具,它不会主动检测或阻止对共享资源的无锁访问:
- 只有当调用
Lock()时发现锁已被持有,才会阻塞当前goroutine; - 只有当调用
Unlock()的goroutine并未持有该锁时,才会触发运行时panic; - 直接访问共享字段(比如
box.Stuff)的操作,完全不受Mutex约束,自然不会产生任何报错。
2. 你的代码逻辑漏洞
你在init()里调用了box.mu.Lock(),但后续的add()和display()都是先执行Unlock(),操作Stuff,再重新Lock()——这意味着在Unlock()到Lock()的这段时间里,锁处于完全释放的状态。add2()和display2()的操作刚好发生在这些解锁窗口中,所以它们能顺利访问Stuff字段,不会被阻塞或报错。
3. 正确的Mutex使用方式
要让Mutex真正保护共享资源,所有访问/修改共享字段的操作都必须在持有锁的状态下进行:
- 进入函数后先调用
Lock(),操作完成后调用Unlock(); - 推荐使用
defer box.mu.Unlock()确保锁被正确释放,避免因代码分支遗漏解锁; - 不要在持有锁的情况下执行IO、sleep等耗时操作,防止阻塞其他goroutine。
修正后的示例代码:
package main import ( "fmt" "sync" ) type Box struct { Stuff int mu sync.Mutex } var box Box func (box *Box) add() { fmt.Println("add()") box.mu.Lock() defer box.mu.Unlock() box.Stuff++ } func (box *Box) display() { fmt.Print("display(): ") box.mu.Lock() defer box.mu.Unlock() fmt.Println(box.Stuff) } func add2() { fmt.Println("add2()") box.mu.Lock() defer box.mu.Unlock() box.Stuff++ // 现在必须持有锁才能操作 } func display2() { fmt.Print("display2(): ") box.mu.Lock() defer box.mu.Unlock() fmt.Println(box.Stuff) // 现在必须持有锁才能读取 } func main() { box.display() box.add() box.display() display2() add2() box.display() }
内容的提问来源于stack exchange,提问作者pigfox
相关产品推荐
相关产品推荐

