简述Go中的Context
简述 Go 中的 Context
起因是笔者对业务代码中频繁出现的 ctx,context.Context,context.background()的不解,但又因为太懒加摆烂一直没有深究,故今天在此处认真恶补一下
简要介绍
Context 字面义上下文
在 Go 中,Context 是一个接口,其可以控制 goroutine 生命周期与关闭,同时也可以作为一个传递上下文的工具,将需要的键值对通过 Context.Value 储存并传递
Context的定义
1 | type Context interface{ |
让我们依次来查看这些定义:
- Done() <- chan struct{}
Done()方法通过返回一个只读通道来判断 Context 是否有效,如果通道关闭,则 Context 失效
- Err() error
返回 Context 被取消的原因
Canceled: 上下文被取消
*DeadlineExceeded:*上下文截止时间已到
-
Deadline() (deadline time.Time, ok bool)
返回设置的截止时间,如果未设置 Context 的截止时间,则 ok 为 false
4. Value(key interface{}) interface{}
返回与键关联的值
可以近似看成一个树状 map 的键值对储存系统,具有层次,当前 context 查询不到所需键时就会自动向上递归,且仅支持修改当前 context
Context 的创建方法
从上面的 Value 方法 介绍中我们可以看到,Context 是树状结构,自动向上递归查询键值,因此必然存在一个根 Context
接下来阐述如何创建一个空的根 Context
-
context.Background()
1 | ctx := context.Background() |
如此,ctx 便是一个空的根 Context,其永不取消,也没有截止时间
-
context.TODO()
1 | ctx := context.Background() |
值得一提的是 Background 和 TODO 二者完全相同,唯一的区别是 TODO 在语义上的表达是暂不清楚如何正确在此处使用 Context,作为标记后续需要被替换成正确的 Context
-
可取消的 Context - cancelCtx
context.WithCancel(parentCtx)
1 | ctx,cancel := context.WithCancel(parentCtx) |
调用 context.WithCancel 会返回一个子 context 以及取消 cancel 的函数
可取消的 Context 允许我们通过返回的 cancel 函数手动取消 Context,同时也可以通过取消父 Context 自动取消所有子 Context
-
定时取消的 Context - timerCtx
有两种类型的 timerCtx
1. 在指定的截止时间取消
context.WithDeadline()
1 | ctx, cancel := context.WithDeadline(ParentCtx, time.Data(2024,12,27,42,42,42,42,UTC)) |
- 在指定持续时间后结束
context.WithTimeout()
1 | // 在指定持续时间后取消 |
-
携带值的 Context - ValueCtx
context.WithValue()
1 | // 创建携带值的Context |
浅入 Context 机制
Context 机制是 Go 语言实现请求取消、超时控制和跨 API 边界传值的重要组件
-
context 树
Context 是以树形结构组织的:
graph TD A[Background] --> B[WithValue] A --> C[WithCancel] B --> D[WithTimeout] C --> E[WithValue]
- 每个Context都可以有多个子Context
- 当父Context取消时,其所有子Context都会被取消
- 子Context的取消不会影响父Context及其他兄弟Context
1
2
3
4
5
Background
/ \
WithValue WithCancel
/ \
WithTimeout WithValue
总而言之,父影响子而子不影响父
TODO :
Go官方Context文档