sync.RWMutex不是更高级的锁,而是专为读多写少场景设计的取舍型工具:读可并发、写须独占、写锁优先;RLock/RUnlock必须成对且不可跨函数边界,零值可用但禁止复制。

直接说结论:sync.RWMutex 不是“比 sync.Mutex 更高级的锁”,而是针对「读多写少」场景做了明确取舍的专用工具——读可以并发,写必须独占,且写锁优先级高于读锁。
RLock 和 RUnlock 必须成对出现,且不能在未加锁时调用 RUnlock
常见错误是把 RUnlock 放在 defer 里,但函数提前 return 导致 RLock 没执行,RUnlock 却被触发:
- 会 panic:fatal error: sync: RUnlock of unlocked RWMutex
- 即使 Go 1.3+ 对未加读锁就
RUnlock的行为更宽容(某些情况不 panic),也不代表它是安全的——它可能掩盖逻辑错误 - 正确做法:确保每个
RUnlock前都有对应、已成功执行的RLock;用defer仅在RLock成功后才注册
Lock 和 Unlock 不能嵌套,RLock 可重入但 RUnlock 多解会出问题
Lock 是非重入的:同一个 goroutine 连续两次调用 Lock 会导致死锁;而 RLock 允许同 goroutine 多次获取(即重入),但 RUnlock 次数不能超过 RLock 次数太多:
- 标准库允许最多多一次
RUnlock(比如RLock一次,RUnlock两次),但这属于未定义行为边界,不应依赖 - 典型坑:在循环中误写
for range { rw.RLock(); ...; rw.RUnlock() },但循环体里又调了另一个也带RLock/RUnlock的函数,导致解锁错位 - 建议:一个作用域内只做一次
RLock+ 一次RUnlock,避免嵌套或分散管理
写锁阻塞新读锁,但不中断已有读操作
这是 sync.RWMutex 的写优先设计核心表现:
- 当有 goroutine 正在执行
Lock并等待时,后续任何RLock调用都会被阻塞,直到写锁释放 - 但已在运行的读操作(已成功
RLock)不受影响,可继续执行并RUnlock - 这意味着:写操作不会“打断”读,但会“排队等所有当前读结束 + 拦住新读”,防止写饥饿
- 如果你观察到读协程大量卡在
RLock,先检查是否有长时写操作(比如写数据库、序列化大结构体)没及时Unlock
最易被忽略的一点:sync.RWMutex 零值可用,但不可复制。一旦把它作为 struct 字段或传参时用了值传递(而不是指针),就会复制一份锁状态,导致并发控制完全失效——这种 bug 很难复现,却极难排查。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











