Go高频计数器须用atomic.AddInt64等原子操作替代普通++,且统一使用int64(跨平台一致)、包级变量、自然对齐;推荐atomic.Int64类型;Prometheus暴露指标应使用GaugeFunc避免锁。

Go 语言中用 atomic 优化高频计数器,核心就一条:用 atomic.AddInt64 替代 counter++,且所有读写必须走原子接口,不能混用普通操作。
为什么必须用 int64 而不是 int
atomic 包不提供 atomic.AddInt,因为 int 在 32 位系统是 4 字节、64 位系统是 8 字节,行为不可控。而 int64 始终是 8 字节,能被 CPU 原生原子指令(如 x86 的 LOCK XADD)直接支持,跨平台一致。
- 声明必须写
var reqCount int64,不能用:=推导为int - 局部变量取地址传给 atomic 会编译失败,所以计数器得是包级变量或堆上分配
- 结构体中若嵌入
int64字段,建议放开头或用//go:align 8确保自然对齐,避免 ARM 平台 panic
读写都得走 atomic 函数
只加不用锁,但读也不能“图方便”。直接 reqCount 读可能看到过期缓存值——多核下 goroutine A 刚加完 1,B 立刻读却还是旧值。
- 递增统一用
atomic.AddInt64(&reqCount, 1) - 读取必须用
atomic.LoadInt64(&reqCount),不是reqCount - 重置用
atomic.StoreInt64(&reqCount, 0),别写reqCount = 0 - 条件更新(比如“只在为 0 时设成 100”)用
atomic.CompareAndSwapInt64,失败需手动重试
Go 1.19+ 推荐用 atomic.Int64 类型
它比老式函数接口更安全、更简洁,自动防住传错类型、漏取地址等低级错误。
立即学习“go语言免费学习笔记(深入)”;
var counter atomic.Int64-
counter.Add(1)替代atomic.AddInt64(&c, 1) -
counter.Load()替代atomic.LoadInt64(&c) -
counter.CompareAndSwap(0, 100)语义更清晰
暴露指标时别让 Prometheus 拖慢 atomic
直接把 atomic.LoadInt64(&count) 塞进 prometheus.NewCounterVec 会触发内部锁,白费 atomic 的无锁优势。
- 正确做法是用
prometheus.NewGaugeFunc,只在采集时调用一次原子读 - 代码示例:
prometheus.MustRegister(prometheus.NewGaugeFunc(
prometheus.GaugeOpts{ Name: "http_requests_total", Help: "Total HTTP requests" },
func() float64 { return float64(atomic.LoadInt64(&reqCount)) },
)) - Prometheus 默认每 15 秒拉取一次,远低于高频写入节奏,完全不构成瓶颈


















