sync.mutex必须与被保护数据同生命周期,值接收者导致锁副本无效,应使用指针接收者;推荐将锁定义为结构体字段,避免函数内声明;用defer mu.unlock()防漏解锁,读多写少时优先rwmutex。

sync.Mutex 必须和被保护的数据共存于同一生命周期内,否则锁无效或导致死锁 —— 这不是配置问题,是结构设计问题。
为什么 func (u User) UpdateName 里的 u.mu.Lock() 没用
值接收者会拷贝整个结构体,u 是副本,u.mu 也是副本。锁这个副本对原对象零影响。
- 所有含
sync.Mutex字段的结构体方法,必须用指针接收者:func (u *User) UpdateName(name string) - 哪怕结构体只有两个字段,也别图省事写成值接收者
-
go vet不报这种错,得靠人工检查接收者类型
锁该定义在哪儿:包级变量、结构体字段、还是函数里
锁的作用域必须覆盖所有可能并发访问共享数据的 goroutine。
- ✅ 推荐:作为结构体字段(如
type Counter struct { mu sync.Mutex; val int }),锁与数据同生命周期 - ⚠️ 可用但需谨慎:包级
var mu sync.Mutex,适合保护全局变量,但易成性能瓶颈 - ❌ 绝对禁止:在函数内部声明
var mu sync.Mutex,每次调用都是新锁,完全不跨 goroutine 生效
如何避免 Unlock 遗漏或 panic 导致死锁
sync.Mutex 不支持重入,且未配对 Unlock 会导致后续 goroutine 永久阻塞。
- 优先用
defer mu.Unlock(),但必须紧跟在mu.Lock()后面,否则提前 return 时 defer 不执行 - 复杂逻辑建议用小作用域包裹:
{ mu.Lock(); defer mu.Unlock(); /* work */ } - panic 场景下
defer仍会执行,所以Lock/defer Unlock是安全组合 -
-race能检测竞态,但不报“漏 unlock”,需代码审查或静态分析工具辅助
读多写少时,sync.RWMutex 比 sync.Mutex 更合适
用 sync.Mutex 会让所有 goroutine(包括只读的)排队,而 sync.RWMutex 允许多个读并发。
- 写操作用
RLock()/RUnlock(),读操作用Lock()/Unlock() - 注意:
RUnlock()必须和RLock()成对,不能混用Unlock() - 如果读操作占比超 70%,切换到
RWMutex通常能提升吞吐量
最容易被忽略的是锁和数据的绑定关系——不是“加了锁就安全”,而是“锁是否真正在保护你认为它在保护的那个变量”。结构体字段方式最不容易出错,其他方式都要反复确认作用域和生命周期是否对齐。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











