
atomic.LoadUint64 为什么总返回 0?
常见现象是:多个 goroutine 并发写 counter,主线程用 atomic.LoadUint64(&counter) 读,但一直看到 0。根本原因不是函数错了,而是读写没对齐——写操作用了非原子方式(比如直接赋值 counter = 100),导致写入没被原子读感知。
- 所有对原子变量的修改,必须用
atomic.StoreUint64、atomic.AddUint64等原子写函数,禁止裸赋值 -
atomic.Load*只保证“读那一刻的快照”,不阻塞也不同步其他内存操作;若需更强语义(如读到最新写),得配合sync/atomic的内存序说明(默认是Relaxed) - 类型必须严格匹配:不能对
uint32变量调atomic.LoadUint64,会 panic 或读错内存
什么时候该用 atomic 而不是 mutex?
核心判断标准就一条:是否只做「单个变量的简单读写或算术更新」。比如计数器、开关标志、指针替换。一旦涉及多个变量联动(如“先改 A 再改 B”)、条件判断后写入、或需要执行一段逻辑临界区,立刻换 sync.Mutex。
- atomic 更轻量,无锁,适合高并发低冲突场景(如每秒百万次计数累加)
- 但不提供“临界区保护”,没法阻止其他 goroutine 同时修改关联数据
- 注意:Go 1.19+ 对
atomic.Pointer和atomic.Int64等类型做了泛型封装,推荐优先用这些类型而非裸函数,可读性更好、类型更安全
atomic.CompareAndSwapInt32 失败却不报错?
这个函数返回 bool,不 panic,也不抛 error——失败就是静默返回 false。很多人忽略返回值,结果逻辑卡在旧值上不动了。
- 典型误用:
atomic.CompareAndSwapInt32(&state, 0, 1)只调一次,如果 state 已是 1,后续逻辑就跳过了 - 正确做法是循环重试(CAS loop):
for { old := atomic.LoadInt32(&state) if old != 0 { break } if atomic.CompareAndSwapInt32(&state, 0, 1) { break } } - CAS 不是万能锁替代品:它适合“乐观更新”,但若竞争激烈,自旋开销可能反超 mutex
atomic.Value 在存 map/slice 时为啥 panic?
atomic.Value 只允许存“可安全拷贝”的值,而 map、slice、func 是引用类型,底层包含指针;直接 Store 会导致运行时 panic:fatal error: sync/atomic: store of inconsistently typed value into Value。
立即学习“go语言免费学习笔记(深入)”;
- 解决方法只有一种:把 map/slice 包进结构体,且结构体字段必须是值类型或指针(推荐指针)
- 示例:
type Config struct { data map[string]string } v := atomic.Value{} v.Store(&Config{data: make(map[string]string)}) // 存指针,安全 - 别试图用
atomic.Value替代sync.RWMutex做频繁读写——它适合“读多写少 + 整体替换”的场景,比如配置热更新
atomic 的关键,是始终记住它只管一个变量、不保顺序、不带锁语义。写错一行非原子赋值,或者漏判 CAS 返回值,性能优势瞬间归零。











