atomic.LoadInt64读不到最新值是因为普通读写绕过内存屏障,必须与atomic.StoreInt64配对使用;atomic.AddInt64是硬件级原子指令,替代非原子的counter++;CAS失败是正常并发反馈,需循环重试;指针原子操作须严格类型一致;原子变量必须有稳定地址。
直接用 *p 读一个被 atomic.StoreInt64 写过的变量,大概率读到旧值——这不是 bug,是绕过了内存屏障。Go 的 atomic 操作靠底层指令加内存序保证可见性,普通读写不参与这套同步机制。
atomic.StoreInt64(&x, v),读就必须用 atomic.LoadInt64(&x),不能混用x 是包级变量、类型完全匹配,fmt.Println(x) 也属于“普通读”,不可靠counter++ 在汇编层是“读 → 加1 → 写”三步,中间可能被其他 goroutine 插入;而 atomic.AddInt64 是单条 CPU 原子指令(如 x86 的 LOCK XADD),硬件保证整个操作不可分割。
int64 在 32 位系统上不是自然对齐的,必须用 atomic 函数访问,否则 panic 或数据错乱*int —— int 是平台相关类型,应显式用 int64 或 int32,再取地址atomic.CompareAndSwapInt64(&x, old, new) 返回 false,只说明“此刻 x 的值不是 old”,不代表出错。这是无锁编程的设计前提,你得自己决定是否重试。
runtime.Gosched() 让出时间片,或短休眠(time.Sleep(1ns))缓解 CPU 占用var tmp int64 = atomic.LoadInt64(&x) 然后传地址——局部变量地址生命周期不够,行为未定义
atomic.StorePointer 和 atomic.LoadPointer 操作的是 unsafe.Pointer,不检查类型。存了 *Config 却当 *User 读,会直接崩溃或读出垃圾内存。
unsafe.Pointer(&cfg),其中 cfg 是具体类型的变量或指针(*Config)(atomic.LoadPointer(&ptr)),括号里的类型要和存入时完全一致unsafe.Pointer,容易丢失类型上下文;Go 1.19+ 可用泛型封装一层类型安全 wrapper最常被忽略的一点:所有原子变量必须有稳定地址。包级变量 OK,new(uint64) 分配的堆内存 OK,但切片元素、map value、结构体非导出字段、局部变量取地址——全都危险。不是编译不过,是运行时行为不可控。