关于Go语言RWMutex(读写互斥锁)使用的困惑求助
搞定Go中RWMutex的正确打开方式,实现你的读写控制需求
兄弟,我太懂你这种困惑了!Go的RWMutex看似简单,但实际用起来很容易踩锁的逻辑坑,尤其是读锁和写锁的互斥规则没搞清楚的时候。先给你理清楚核心逻辑,再给你一个能完美实现你需求的示例代码。
先搞懂RWMutex的核心规则
这三点是你必须记牢的:
- 写锁是完全排他的:只要有一个goroutine拿到了
Lock()(写锁),其他所有goroutine不管是想拿读锁RLock()还是写锁Lock(),都会被阻塞,直到写锁被Unlock()释放。 - 读锁是共享的:多个goroutine可以同时持有
RLock()(读锁),并行执行读操作,互不干扰。但如果此时有goroutine想拿写锁,它会被阻塞,直到所有读锁都被RUnlock()释放。 - 你的问题根源:大概率是读操作没加读锁!这样就算写锁生效了,读操作还是直接裸访问map,根本没被锁住;如果错误地给读操作加了写锁,那读操作就变成串行的了,自然“反而更糟”。
实现你需求的正确代码示例
下面是一个封装好的安全Map,完全符合你“读操作并行,写操作时所有读都被锁住”的要求:
import "sync" // SafeMap 带读写锁的安全Map type SafeMap struct { mu sync.RWMutex data map[string]int // 这里可以替换成你实际需要的结构体类型,比如包含age字段的结构 } // NewSafeMap 创建一个初始化好的SafeMap func NewSafeMap() *SafeMap { return &SafeMap{ data: make(map[string]int), } } // Get 读操作:使用读锁,支持多goroutine并行读取 func (m *SafeMap) Get(key string) (int, bool) { m.mu.RLock() // 加读锁 defer m.mu.RUnlock() // 用defer确保锁一定会被释放,避免死锁 val, ok := m.data[key] return val, ok } // Set 写操作:使用写锁,排他性,此时所有读/写操作都会被阻塞 func (m *SafeMap) Set(key string, val int) { m.mu.Lock() // 加写锁 defer m.mu.Unlock() // 确保锁释放 m.data[key] = val }
代码逻辑解释
- 当多个goroutine同时调用
Get()时,它们会各自拿到读锁,并行执行读操作,效率拉满。 - 当某个goroutine调用
Set()时,它会先抢占到写锁,此时所有后续的Get()和Set()都会进入阻塞状态,直到Set()执行完毕并释放写锁,完美实现“写时所有读加锁”的需求。
避坑提醒
- 所有读操作必须加读锁:哪怕你觉得读操作是“只读”的,也必须加
RLock(),否则会出现数据竞争,写操作的锁根本管不住裸读的goroutine。 - 不要滥用写锁:读操作就用读锁,写操作才用写锁,不然完全浪费了RWMutex支持并行读的优势。
- 用defer释放锁:这是最稳妥的方式,避免因为代码分支或者panic导致锁没释放,进而引发死锁。
内容的提问来源于stack exchange,提问作者Francesco
相关产品推荐
相关产品推荐

