简述 Go 中的 Context

起因是笔者对业务代码中频繁出现的 ctx,context.Context,context.background()的不解,但又因为太懒加摆烂一直没有深究,故今天在此处认真恶补一下

简要介绍

Context 字面义上下文

在 Go 中,Context 是一个接口,其可以控制 goroutine 生命周期与关闭,同时也可以作为一个传递上下文的工具,将需要的键值对通过 Context.Value 储存并传递

Context的定义
1
2
3
4
5
6
7
8
9
type Context interface{
Done() <- chan struct{}

Err() error

Deadline() (deadline time.Time, ok bool)

Value(key any) any
}

让我们依次来查看这些定义:

  1. Done() <- chan struct{}

​ Done()方法通过返回一个只读通道来判断 Context 是否有效,如果通道关闭,则 Context 失效

  1. Err() error

​ 返回 Context 被取消的原因

Canceled: 上下文被取消

*DeadlineExceeded:*上下文截止时间已到

  1. Deadline() (deadline time.Time, ok bool)

    返回设置的截止时间,如果未设置 Context 的截止时间,则 ok 为 false

4. Value(key interface{}) interface{}

​ 返回与键关联的值

​ 可以近似看成一个树状 map 的键值对储存系统,具有层次,当前 context 查询不到所需键时就会自动向上递归,且仅支持修改当前 context

Context 的创建方法

从上面的 Value 方法 介绍中我们可以看到,Context 是树状结构,自动向上递归查询键值,因此必然存在一个根 Context

接下来阐述如何创建一个空的根 Context

  1. context.Background()

1
ctx := context.Background()

​ 如此,ctx 便是一个空的根 Context,其永不取消,也没有截止时间

  1. context.TODO()

1
ctx := context.Background()

值得一提的是 Background 和 TODO 二者完全相同,唯一的区别是 TODO 在语义上的表达是暂不清楚如何正确在此处使用 Context,作为标记后续需要被替换成正确的 Context

  1. 可取消的 Context - cancelCtx

​ context.WithCancel(parentCtx)

1
2
3
4
ctx,cancel := context.WithCancel(parentCtx)

// 调用cancel函数以取消context
defer cancel()

调用 context.WithCancel 会返回一个子 context 以及取消 cancel 的函数

可取消的 Context 允许我们通过返回的 cancel 函数手动取消 Context,同时也可以通过取消父 Context 自动取消所有子 Context

  1. 定时取消的 Context - timerCtx

​ 有两种类型的 timerCtx

1.  在指定的截止时间取消

​ context.WithDeadline()

1
2
3
4
ctx, cancel := context.WithDeadline(ParentCtx, time.Data(2024,12,27,42,42,42,42,UTC))
// 创造一个在data取消的Context,参数依次为 年,月,日,时,分,秒,纳秒,时区

defer cancel()
  1. 在指定持续时间后结束

​ context.WithTimeout()

1
2
3
// 在指定持续时间后取消
ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second)
defer cancel()
  1. 携带值的 Context - ValueCtx

​ context.WithValue()

1
2
3
4
5
// 创建携带值的Context
ctx := context.WithValue(parentCtx, Key, Value)

// 通过key获取对应的值
value := ctx.Value(Key)

浅入 Context 机制

Context 机制是 Go 语言实现请求取消、超时控制和跨 API 边界传值的重要组件

  1. context 树

    Context 是以树形结构组织的:

- 每个Context都可以有多个子Context

- 当父Context取消时,其所有子Context都会被取消

- 子Context的取消不会影响父Context及其他兄弟Context

1
2
3
4
5
       Background
/ \
WithValue WithCancel
/ \
WithTimeout WithValue

总而言之,父影响子而子不影响父

TODO :

Go官方Context文档