Go Context 到底放第一个参数传,还是放结构体里?

大家好,我是煎鱼。

前段时间我们在聊手动管理内存 arena 的后续时。我们有提到 context 的函数传参等问题。

当时在评论区有许多的小伙伴交流了起来,大家对此还是非常关注的:

今天我们就来聊一聊 conetxt 传参的这个事。到底摆哪?Go 官方推荐是什么?

快速介绍

上下文(Context)是 Go 语言中非常有特色的一个特性,其主要的作用是在 goroutine 中进行上下文的传递,而在传递信息中又包含了 goroutine 的运行控制、上下文信息传递等功能。

以下是一个简单 Demo:

go 复制代码
func main() {
	parentCtx := context.Background()
	ctx, cancel := context.WithTimeout(parentCtx, 1*time.Millisecond)
	defer cancel()

	select {
	case <-time.After(1 * time.Second):
		f1(ctx)
	case <-ctx.Done():
		f2(ctx)
	}
}

func f1(ctx context.Context) {
	fmt.Println("脑子进煎鱼了", ctx.Err())
}

func f2(ctx context.Context) {
	fmt.Println("煎鱼进脑子了", ctx.Err())
}

输出结果:

煎鱼进脑子了 context deadline exceeded

Go context 放哪好的争议点

根据上面的 Demo,有争议的点在哪?就是这个函数里 context,到底怎么传。放函数第一个参数,还是放结构体?

go 复制代码
// 第一种方式
func f1(ctx context.Context) {...}

// 第二种方式
type T struct {
  Ctx context
}
  • 放在函数第一个参数 context 中的话,那每个方法都需要进行传递,用起来就像被 "污染" 了一样。每个函数都要层层递进的传下去。非常的显性。
  • 放在结构体中的话,按理说 context 是基本对标请求的,每次请求都是不同的 context。这样可能会导致每次都要 NewT() 或是 WithContext。一旦忘了,就很容易埋下隐患。

Go 官方建议

Go 官方建议中是怎么说的?在 context 标准库的文档中有提到。使用上下文的程序应遵循以下规则,以保持跨包的接口一致:

  1. 不要将 context 存储在结构体中。
  2. 应该将 context 显式传递给每个需要它的函数,其应该是第一个参数,通常将参数命名为 ctx。
  3. 不要传递 nil context。如果您不确定要使用哪个 context,请传递 context.TODO

对应的示例代码:

go 复制代码
func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}

划重点:官方建议在函数首位参数传递 context,不建议使用结构体传递。

一些讨论

虽然官方是这么建议了。但看起来没头没尾,不认可的人也有不少。

包括前 Go 核心团队的 @Brad Fitzpatrick 也表达了反对的意见,并给出进一步的诠释:

While we've told people not to add contexts to structs, I think that guidance is over-aggressive. The real advice is not to store contexts. They should be passed along like parameters. But if the struct is essentially just a parameter, it's okay. I think this concern can be addressed with package-level documentation and examples.

大概意思:我们曾告诉人们不要在结构体中添加上下文,但我认为这种指导过于激进。真正的建议是不要存储上下文。它们应该像参数一样传递。但如果结构体本质上只是一个参数,那就没有问题。我认为可以通过包级文档和示例来解决这个问题。

可能有同学想说,那是不是 Go 官方就可以放宽 context 的使用建议了?

想太多了...早在 2017 年就有 @Caleb Spare 提出《context: relax recommendation against putting Contexts in structs》,希望 Go 官方放宽对于 context 放入结构体中的建议。

但还是处于长期的阻塞状态,没有进一步的相关信息。@Russ Cox 被 @ 了也并没有出来说任何的建议。

可以认为官方文档上的态度没有什么变化。

总结

Context 在 Go 中是一个非常重要的特性,基本覆盖编程的方方面面,只要和 "控制" 相关的,都有他的存在。

在官方的建议中,是不推荐使用结构体存储 context 的,更推荐使用函数第一个参数传递 context 并命名为 ctx。进一步诠释的话,指的是不希望存储 context。

大家在 context 实践中又是使用哪种方式呢?欢迎分享。

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

推荐阅读

相关推荐
郝同学的测开笔记9 小时前
云原生探索系列(十二):Go 语言接口详解
后端·云原生·go
一点一木1 天前
WebAssembly:Go 如何优化前端性能
前端·go·webassembly
千羽的编程时光2 天前
【CloudWeGo】字节跳动 Golang 微服务框架 Hertz 集成 Gorm-Gen 实战
go
27669582923 天前
阿里1688 阿里滑块 231滑块 x5sec分析
java·python·go·验证码·1688·阿里滑块·231滑块
Moment4 天前
在 NodeJs 中如何通过子进程与 Golang 进行 IPC 通信 🙄🙄🙄
前端·后端·go
唐僧洗头爱飘柔95275 天前
(Go基础)变量与常量?字面量与变量的较量!
开发语言·后端·golang·go·go语言初上手
黑心萝卜三条杠5 天前
【Go语言】深入理解Go语言:并发、内存管理和垃圾回收
google·程序员·go
不喝水的鱼儿5 天前
【LuatOS】基于WebSocket的同步请求框架
网络·websocket·网络协议·go·luatos·lua5.4
微刻时光5 天前
程序员开发速查表
java·开发语言·python·docker·go·php·编程语言
lidenger6 天前
服务认证-来者何人
后端·go