go语言 Ctx:「错误三:在 Context 中存储可变值」

context.WithValue 看起来像是一个很方便的数据传递方式。对于合适的数据,它确实方便。

但很多人忽略了一个关键约束:放进 context 的值应该是不可变的

如果你把一个可变指针放进 context,然后通过 context 取出来修改,那基本就是在等 data race 发生。

如果多个 goroutine 共享同一个 context ,并且都调用 MetricsFromContext 修改这个结构体,就会产生数据竞争。

Go 的 race detector 可以在测试中发现这类问题,但前提是你的测试真的覆盖到了并发路径。

如果你确实需要并发安全的修改,那么这个 value 自身应该包含锁:

对于简单计数器,更好的做法是使用 atomic:

更大的原则是:

如果 context 中的某个值需要被修改,那么这个修改行为必须是显式且线程安全的,而不是依赖约定。

辅助理解:

1.context 中的某个值需要被修改 就是前面例子:从 ctx 取出*RequestMetrics,要修改它里面的字段。
2.显式

❌坏写法(隐式):

Go 复制代码
m := MetricsFromContext(ctx)
m.DBQueries++ // 直接裸写 ++,随手改结构体字段

这种修改是偷偷摸摸 的,看代码第一眼不知道这行会修改共享对象,全靠程序员口头约定:"大家注意,这个对象多协程不要乱改"。👉这就是依赖约定

✅显式写法:把修改封装成方法,调用方法去改

Go 复制代码
m.RecordDBQuery() // 一看就知道:这里要做修改操作

修改动作不再是普通++,而是调用一个专门的函数,代码阅读者一眼识别:这里会变更状态。

3.线程安全

方法内部必须保护并发:sync.Mutex上锁 / atomic原子类型。 不能裸操作普通 int 字段。

4.不要依赖约定

依赖约定:团队口头、文档写一句 "警告,这个 metrics 不要直接改字段!"

人的约定是不可靠的:

  • 新来的同事没看文档;
  • 过几个月自己写代码忘记这个坑;
  • 代码重构,随手写一行m.DBQueries++,直接引入 data‑race。

靠人自觉约束,迟早会翻车。要把安全约束写进代码本身,而不是靠人的记性。

5.正反对比

❌反面:依赖约定(危险)

✅正面:显式 + 线程安全(符合这条原则)

使用:

就算有人想乱改,他也要主动写m.DBQueries++;只要团队代码规范不允许直接访问,错误写法很容易被 code review 抓到。更好做法:字段小写不导出,外部包完全访问不到。

Go 复制代码
type RequestMetrics struct {
	mu        sync.Mutex
	dbQueries int // 小写,外部包无法直接修改!只能调用方法
}

一句话总结这条大原则

不要指望所有人记住 "不要直接改 ctx 拿出来的对象" 这种口头约定。 如果一定要往 ctx 里面放要修改的对象:

  1. 修改操作封装成专门的方法调用(显式)
  2. 方法内部用锁 /atomic 保证并发安全
  3. 最好结构体字段不对外暴露,从语法层面阻止裸修改。
    终极最佳实践:可变对象干脆不要塞 context,直接函数参数传递,从根源避开整套坑
相关推荐
kruptos2 小时前
分布式系统怎么“选主“?Raft 共识一次讲清
开发语言·分布式·php·共识算法
吴声子夜歌3 小时前
ApacheCommons——commons-cli(命令行参数解析)
java·开发语言·apache
爱学堂IT分享3 小时前
鹤翔老师pmp项目管理培训认证考试课 (直播录播题库送学时证明)【共58课时】-51CTO学堂
后端
benchmark_cc4 小时前
REST API 和 Python SDK 应该怎么选?量化交易数据接口选型实战
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
王的宝库4 小时前
Go 项目结构:从单文件到标准工程布局
开发语言·后端·golang
Tairitsu_H4 小时前
[C++] 深入理解红黑树:封装set与map
开发语言·c++·set·map·红黑树·模拟实现
泡海椒4 小时前
JQuick-Curl 性能分析:并发场景下的性能表现与调优,第三方接口调用不只要快写也要稳跑
java·开发语言·okhttp
小智老师PMP4 小时前
2026深度解析|PMP第八版与NPDP核心侧重点本质区别(管理类证书怎么选)
开发语言·分布式·算法·职场和发展·产品经理
花酒锄作田4 小时前
Go - Gin中使用sessions
golang
码事漫谈5 小时前
类型即世界:一个 C++ 程序员的本体论入门
后端