如何在Golang中使用sync包的RWMutex进行读写锁_Golang读写锁的并发控制方法

1次阅读

读多写少场景用RWMutex因允许多读一写,提升并发吞吐;但写频繁或读极短时Mutex更优,且RWMutex不保证读写绝对顺序、不支持TryLock,复合操作需双检锁等上层保障。

如何在Golang中使用sync包的RWMutex进行读写锁_Golang读写锁的并发控制方法

为什么读多写少场景下要用 RWMutex 而不是 Mutex

因为 RWMutex 允许多个 goroutine 同时读,但写操作会独占锁;而 Mutex 无论读写都互斥。在读远多于写的场景(比如配置缓存、状态快照),用 RWMutex 能显著提升并发吞吐。但要注意:它不保证读写之间的“绝对顺序”,比如一个写操作正在等待时,新来的读请求仍可能插队获取读锁——这不是 bug,是设计使然。

RWMutex 的四个核心方法怎么配对使用

必须严格区分读/写路径,否则会死锁或数据竞争:

  • RLock()RUnlock() 成对用于只读操作;不能在持有 Rlock 期间调用 Lock()
  • Lock()Unlock() 成对用于写操作;此时所有新 RLock() 会被阻塞,直到 Unlock()
  • 禁止嵌套:不能在已持 RLock()goroutine 中再调 Lock(),这会永久阻塞
  • 忘记 Unlock()RUnlock() 是常见 panic 来源,建议用 defer mu.RUnlock() 保底

什么时候 RWMutex 反而比 Mutex 更慢

当写操作频繁(比如每秒数百次以上)或读操作极短(纳秒级),RWMutex 的额外状态管理开销(如维护 reader 计数、唤醒策略)可能超过收益。实测发现,在写占比 >15% 且临界区极小时,Mutex 往往更稳。另外,RWMutex 不支持 TryLock 类语义,无法做非阻塞探测,这点常被忽略。

一个易错的典型误用模式

下面这段代码看似合理,实则危险:

立即学习go语言免费学习笔记(深入)”;

func (c *Cache) Get(key string) string {     c.mu.RLock()     v, ok := c.data[key]     c.mu.RUnlock()     if !ok {         // 触发写:但此时 mu 已释放!         c.mu.Lock()         defer c.mu.Unlock()         if _, loaded := c.data[key]; !loaded {             c.data[key] = fetchFromDB(key)         }         return c.data[key]     }     return v }

问题在于:两次锁之间存在竞态窗口——另一个 goroutine 可能在 RUnlock() 后、Lock() 前删掉该 key。正确做法是用双检锁(double-checked locking),或改用 sync.map 处理高频读写混合场景。RWMutex 本身不解决“读-判断-写”这类复合操作的原子性,得靠上层逻辑兜底。

text=ZqhQzanResources