因为gin每个请求在独立goroutine中执行,而count++非原子(读-加-写三步),并发时多个goroutine可能读到相同值并写回覆盖,导致计数丢失;应改用atomic.addint64(&count, 1)保证原子性。

为什么 handler 里改全局变量会出错
因为 Gin 每个请求都在独立 goroutine 里执行,但 count++ 这种操作不是原子的——它实际分三步:读值、加 1、写回。两个 goroutine 同时读到 100,各自加 1 再写回,最终可能只 +1 而不是 +2。
现象就是压测时返回的 count 值远小于请求数,比如发 10000 次请求,结果只有 8723。
用 atomic.AddInt64 替代普通自增
这是最轻量、最安全的方案,适用于计数器、状态标志等简单整型操作。
-
count必须声明为int64类型(atomic对int不提供直接支持) - 用
atomic.AddInt64(&count, 1)替代count++,它由 CPU 指令保证不可中断 - 读取时用
atomic.LoadInt64(&count),避免读到中间态 - 注意:不能对
atomic变量取地址再传给其他函数做非原子操作
什么时候该用 sync.Mutex 而不是 atomic
当你要保护的不止一个变量,或者逻辑涉及多步判断+修改(比如“如果余额 > 100 才扣款”),atomic 就不够用了。
- 定义一个
sync.Mutex实例,比如var mu sync.Mutex - 所有访问共享数据的代码块前加
mu.Lock(),结束后立刻mu.Unlock() - 别在 defer 里 unlock —— 如果 lock 失败或 panic,defer 可能不执行,导致死锁
- 锁粒度要细:只包住真正共享的部分,不要把整个 handler 或 DB 查询包进去
context.Copy() 是异步任务的保命操作
如果你在 handler 里启 goroutine 做异步事(比如发通知、记日志),直接传入原始 c 会崩溃——因为请求一结束,Gin 就回收了这个 Context 的底层资源。
- 必须调用
c.Copy()得到一个可脱离生命周期的新上下文 - 异步 goroutine 中禁止调用
c.JSON()、c.Abort()等响应相关方法,它们只对原请求有效 - 如果异步任务需要访问请求参数,应在
Copy()前提取并传参,而不是在 goroutine 里读c.Param()











