Golang Go语言中一般 context 声明放在哪里合适

发布于 1周前 作者 songsunli 来自 Go语言

有一个函数,要并发访问好几个数据库,返回时间不一样,就叫做 longTimeTask()吧,我想用 context 设置超时,现在试下了,context 声明放在 main 里面,就所有 goroutine 共享了,不符合我要求,如果这样是可以的:

	go func() {
		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
		defer cancel()
		longTimeTask(ctx)
	}()

后面我发现这样也行:

func longTimeTask(){
		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
		defer cancel()
        }

所以想问问哪种最合适


Golang Go语言中一般 context 声明放在哪里合适

更多关于Golang Go语言中一般 context 声明放在哪里合适的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

9 回复

如果我 CR 的话会觉得第一种好一点,没有那么隐式,因为长任务与超时没有必然联系

更多关于Golang Go语言中一般 context 声明放在哪里合适的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


没有明显区别,要改的话用 IDE 重构功能改也不是特别麻烦。风格这种事,场景多了之后容易分析。


我看其实只有两点区别,传不传入底层 context ,以及谁来控制 3s 的这个 threshold

用入参显示声明就好

从工程化的角度来看,尽量不要引入 implicit variable ,这会增加耦合性和复杂性。如果上下文 context 跟当前函数是高内聚,天然耦合性强的,就直接定义为私有变量,没问题,也就是第二种;但如果 context 会在其他地方被引用,或需要控制,就必须放在外层,作为参数被调用,或者用函数传入,也就是第一种。

总的来看,要视情况而定,单从代码量来看,第二种 4 行代码肯定优于第一种 5 行代码 🐶

一般服务里是一个请求一个 context ,一般框架会直接给你准备好,你一路传下去就行,如果框架没有的话就自己在处理一开始的时候新建一个

这个跟 context 没有关系,还是看业务,如果 longTimeTask 是需要外部来控制 timeout 就是第一种,如果完全不需要外部控制就是第二种

第一种合适,第一种的含义是在主 goroutine 中指定所有派生的 goroutine 都必须在(主 goroutine 视角) 3 秒内做完;第二种意思是主 goroutine 派生一堆 goroutine ,不管多久能做完;但是每个派生 goroutine 单独控制超时 3 秒。

我觉得虽然最终实现出来的效果可能没什么差别,但是从操作语义上觉得还是主 goroutine 统一管控比较好。

最后写出来的效果应该像这样(随手写的,很可能跑不通,但是琢磨应该大致结构大差不差):
- [https://go.dev/play/p/A1ae5LNzsQu]( https://go.dev/play/p/A1ae5LNzsQu)

go<br>package main<br><br>import "fmt"<br><br>func main() {<br><br> ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)<br> defer cancel()<br><br> wg := sync.WaitGroup{} <br> for i:= 0; i &lt; 10; i++ {<br> wg.Add(1)<br> go longTimeTask(ctx context.Context) {<br> // some DB query here<br> wg.Done()<br> return<br> }(ctx)<br> }<br> <br> wg.Wait()<br>}}<br><br>

6L 正解.

以上其他 L, 扯淡.

鉴于提问在 go 标签下, go 的 Context, 主要有 2 种用法.

1. 用于替代全局变量, 更安全的透传"偏全局的"参数. 常用于: web 的 http Ctx, 携带 http 请求参数, 并在透传中, 注入新的参数, 向下传.
2. 并发控制. 更优雅的控制 Goroutine 退出. 常用于: db/redis/mq/rpc 等中间件 client 的退出管理.

多看一些 web framework 源码, 在 graceful shutdown 处, 都可看到 context 的典型用法.


其他语言, python 的 django http request 的源码, 也有类似设计.

Context, 是一种设计范式. 至于是要在 main 全局定义, 还是局部定义, 是具体业务场景决定的. 具体问题, 具体分析.

我给出的 2 种用法, 就存在 main 全局定义的 ctx, 也存在定义在局部的 ctx.



PS:

不懂, 就不要强答, 误人子弟.

写代码, 不是八股文. 要搞清楚本质.

错的答案, 比不回答. 更糟糕.

在Golang(Go语言)中,context 的声明位置通常取决于其使用场景和代码结构。以下是一些常见的最佳实践和建议:

  1. 函数参数: 如果 context 需要在多个函数之间传递,通常将其作为函数参数传递。这有助于在调用链中保持 context 的可见性和可控性。在HTTP处理函数中,context 常作为第一个参数传入,以便在整个请求处理过程中使用。

  2. 包级别变量: 在特定情况下,如果 context 的生命周期与整个包相关(例如,用于数据库连接或日志记录),可以将其声明为包级别变量。然而,这种做法应谨慎使用,以避免在包的不同部分之间产生不必要的依赖和副作用。

  3. 局部变量: 对于仅在单个函数或代码块中使用的 context,可以在需要时声明为局部变量。这有助于限制 context 的作用域,并减少潜在的错误和复杂性。

  4. 初始化函数: 在某些复杂的应用中,可能会使用初始化函数(如 init 函数)来设置全局 context。这通常用于配置和设置全局状态,但同样需要谨慎使用以避免全局状态的滥用。

总之,context 的声明位置应根据具体的使用场景和代码结构来确定。在大多数情况下,将其作为函数参数传递是最灵活和可控的做法。

回到顶部