Go net/http与Gin高并发场景下计数输出重复问题排查
嘿,这个问题我之前做高并发测试的时候也踩过坑!咱们先把核心原因说清楚,再给你解决办法~
问题核心原因
你遇到的重复输出,根本不是打印环节的并发安全问题,而是你获取要打印的count值的方式破坏了原子性:
atomic包的单个操作是原子安全的,但如果把「原子累加」和「获取当前值」拆成两个独立的原子操作(比如先Add,再Load),这两个操作之间没有原子性保证。- 举个直观的场景:
- Goroutine 1 执行
atomic.AddInt64(&count, 1),把count从5变成6 - Goroutine 2 立刻抢占执行
atomic.AddInt64(&count, 1),把count从6变成7 - Goroutine 1 再执行
atomic.LoadInt64(&count),拿到的是7而不是它自己累加后的6 - Goroutine 2 执行
atomic.LoadInt64(&count),拿到的也是7 - 最终两个goroutine都打印7,就出现了重复
- Goroutine 1 执行
哪怕你把值复制到goroutine里打印,或是用并发安全的log包输出也没用——因为你要打印的值在获取的时候就已经是重复的了。
正确的解决方式
直接用atomic.Add的返回值!这个返回值是原子累加操作的即时结果,和累加操作绑定在一起,完全原子安全:
var count int64 func handler(w http.ResponseWriter, r *http.Request) { // Add操作会返回累加后的新值,这个值是当前goroutine原子操作后的准确结果 newCount := atomic.AddInt64(&count, 1) // 不管用fmt还是log打印,这个newCount都是唯一的,不会重复 log.Println(newCount) w.Write([]byte("ok")) }
这样每个请求对应的goroutine拿到的newCount都是它自己累加后的那个唯一值,绝对不会出现重复输出。
额外注意点
- 确保你的
count变量是int64(或者uint64)类型,atomic包只支持固定宽度的数值类型的原子操作,别用普通的int,否则会有编译错误或者隐性问题。 - 高并发下如果打印太频繁,控制台输出的顺序可能看起来有点乱(因为goroutine调度的随机性),但每个
newCount的值一定是唯一且递增的——这是正常现象,因为打印操作本身是串行的,但累加是并行的。
内容的提问来源于stack exchange,提问作者fcce
相关产品推荐
相关产品推荐

