atomic.addint64需传*int64,整数字面量无地址,局部变量未取址会报错;标志位读写均须原子操作;atomic.value仅适用于写少读多且值不可变的场景;atomic.bool须显式调用方法,不支持布尔上下文。

atomic.AddInt64 为什么不能直接传变量地址?
因为 atomic.AddInt64 要求第一个参数是 *int64 类型,而 Go 中的整数字面量(比如 0)没有地址,结构体字段或局部变量若未取地址也会编译失败。常见错误是写成 atomic.AddInt64(counter, 1),结果报错:cannot take the address of counter(当 counter 是函数参数或短声明的值时)。
实操建议:
- 确保计数器是包级变量或结构体字段,且用指针传入:
atomic.AddInt64(&counter, 1) - 避免对局部变量取地址后传给
atomic函数——逃逸分析可能带来开销,且无实际并发意义 - 如果计数器在 struct 里,字段必须导出(首字母大写)且对齐:Go 1.17+ 要求
int64字段自然对齐(前后无更小类型字段干扰),否则可能 panic 或读写异常
atomic.CompareAndSwapInt32 处理标志位的典型误用
很多人用 atomic.CompareAndSwapInt32 实现“只执行一次”的逻辑,但常忽略初始值和期望值的语义一致性。比如初始化为 0 表示“未设置”,却用 atomic.CompareAndSwapInt32(&flag, 0, 1),看似合理,但若多个 goroutine 同时调用,只有一个能成功——这本身没错;问题出在后续判断上:有人写 if flag == 1 直接读取,绕过了原子读,导致看到脏数据。
实操建议:
- 标志位读取也必须用原子操作:
atomic.LoadInt32(&flag) == 1,不能裸读变量 - 不要复用同一变量表达多种状态(如 0=未开始、1=进行中、2=完成),
CompareAndSwap适合二值切换,多状态建议用atomic.Value或配合互斥锁 - 注意平台差异:ARM 上非对齐访问可能触发 SIGBUS,务必保证
int32字段单独一行、前后无byte或bool
atomic.Value 替代 sync.Mutex 的边界在哪?
atomic.Value 适合“写少读多”且写入值是不可变对象的场景,比如配置热更新、连接池切换。但它不是万能锁替代品——一旦你试图在 Store 里塞一个 map 并后续修改其内容,就彻底失去线程安全。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
实操建议:
- 只 Store 整个新对象,不 Store 指针后修改其指向内容:
v.Store(map[string]int{"a": 1})✅;v.Store(m); m["b"] = 2❌ - 读取后得到的是副本(对 map/slice 等引用类型仍是原底层数组),所以读完立刻用,别缓存引用
- 性能上,
atomic.Value读比sync.RWMutex快得多,但写比sync.Mutex慢——因为内部用了类似自旋+全局锁的降级机制
Go 1.19+ 的 atomic.Bool 怎么避免退化成普通 bool?
atomic.Bool 是 Go 1.19 引入的语法糖,底层仍基于 int32,但封装了常用操作。容易踩的坑是把它当成普通 bool 用:var b atomic.Bool; if b { ... } 会编译失败,因为它没实现布尔上下文转换。
实操建议:
- 必须显式调用方法:
b.Load()获取当前值,b.Store(true)设置,b.Swap(false)原子翻转 - 不要嵌入到 struct 里再用匿名字段方式访问(如
type T struct{ atomic.Bool }),这样t.Load()会找不到方法——Go 不自动提升嵌入字段的方法到外层 - 和
int32版本一样,它要求字段对齐:放在 struct 里时,前面不能有byte、uint16等破坏 4 字节对齐的字段
真正难的不是记住哪些函数可用,而是判断某个字段是否真的需要原子操作——很多所谓“竞争”其实来自逻辑错误,而非缺少原子性。先用 -race 跑一遍,再决定动不动 atomic。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










