Appearance
Go 里的 Mutex、RWMutex、Atomic 怎么选
先说结论
- 普通共享状态保护,优先从
Mutex开始想 - 读远多于写、临界区很短时,
RWMutex才更可能有价值 Atomic更适合非常简单、非常明确的原子读写,不适合承载复杂共享状态
一、三者分别更像什么
1. Mutex
最直接,也最稳。
它的思路就是:这段共享资源同一时刻只让一个协程进来。
优点:
- 语义清晰
- 不容易误用
- 适合大多数共享状态保护
2. RWMutex
它把读锁和写锁分开。
适合:
- 读很多
- 写很少
- 临界区很短
但它不是天然更快,因为:
- 写锁会阻塞所有读
- 竞争模式复杂时收益未必明显
3. Atomic
它更像“极小粒度的无锁原子操作工具”。
适合:
- 计数
- 状态位
- 指针切换
但前提通常是:
你操作的是一个很小、很明确的共享值。
二、为什么很多场景先考虑 Mutex 就够了
因为多数并发问题真正麻烦的不是“锁慢不慢”,而是:
- 共享状态边界清不清楚
- 有没有遗漏加锁
- 是否出现竞态
在这几件事上,Mutex 往往最直接。
如果一上来就为了“更高性能”去拆成 RWMutex 或 Atomic,反而更容易把代码复杂度抬高。
三、什么时候 RWMutex 才真的值得
更适合下面这类场景:
- 缓存 map 读很多,写很少
- 配置快照高频读、低频更新
- 查询操作远大于修改操作
但要注意,只有在读写比例真的明显失衡时,它才更可能带来收益。
四、Atomic 最容易被高估在哪里
很多人看到它“无锁”,就容易觉得它一定更高级。
其实它更适合的是:
- 一个整数
- 一个布尔状态
- 一个指针引用
一旦你要保护的是:
- 多字段对象
- 复合状态
- 需要多个步骤一起一致
Atomic 就不再自然了。
五、最容易踩的坑
1. 读多写少判断失误,盲目上 RWMutex
实际写并不少时,RWMutex 可能并没有明显收益。
2. 用 Atomic 保护复杂对象
单个字段原子了,不代表整个业务状态一致了。
3. 把并发问题理解成“锁种类问题”
很多性能问题根因其实是共享状态设计不合理,不是该换哪种锁。
总结
Go 里的 Mutex、RWMutex、Atomic 没有谁天然更强。大多数场景先从 Mutex 开始,读远多写少再看 RWMutex,共享值极小且语义明确时再用 Atomic,这套顺序通常更稳。