Context
go Context
上下文 context.Context 一般用于在 API 边界之间以及过程之间传递截止时间、取消信号或者其他请求相关的数据。
一个接口
context.Context 是 Go 语言在 1.7 版本中引入标准库的接口,该接口定义了四个需要实现的方法,其中包括:
Deadline
— 返回 context.Context 被取消的时间,也就是完成工作的截止日期;Done
— 返回一个 Channel,这个 Channel 会在当前工作完成或者上下文被取消之后关闭,多次调用 Done 方法会返回同一个 Channel;Err
— 返回 context.Context 结束的原因,它只会在 Done 返回的 Channel 被关闭时才会返回非空的值;- 如果 context.Context 被取消,会返回 Canceled 错误;
- 如果 context.Context 超时,会返回 DeadlineExceeded 错误;
Value
— 从 context.Context 中获取键对应的值,对于同一个上下文来说,多次调用 Value 并传入相同的 Key 会返回相同的结果,该方法可以用来传递请求特定的数据;
四个具体实现
emptyCtx
本质是一个 int。
emptyCtx 永远不会取消,没有值,也没有截止日期。它不是 struct{},因为此类型的变量必须具有不同的地址。cancelCtx
type cancelCtx struct { Context mu sync.Mutex // 用于保护这几个字段的锁,以保证 cancelCtx 是线程安全的 done chan struct{} // 用于获取该 Conext 的取消通知 children map[canceler]struct{} // 用于存储以当前节点为根节点的所有可取消的 Context,以便在根节点取消时可以把它们一并取消 err error // 用于存储取消时指定的错误信息 }
timerCtx
type timerCtx struct { cancelCtx timer *time.Timer // 由 cancelCtx.mu 来保护 deadline time.Time }
在 cancelCtx 的基础上封装了一个定时器和一个截止时间,这样既可以根据需要主动取消,也可以在到达 deadline 是通过 timer 来触发取消动作。
valueCtx
type valueCtx struct { Context key, val interface{} }
六个函数
func Background() Context
func TODO() Context
Background 和 TODO 这两个函数内部都会创建 emptyCtx,
本质上是一样的,主要是语义上的区别。Background 主要用于在初始化是获取一个 Context,而 TODO 函数官方文档建议在本来应该使用外层传递的 ctx,而外层却没有传递的地方使用。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
用于把一个 Context 包装成一个 cancelCtx,并提供一个取消函数,调用它可以取消对应的 Context。
func WithDeadline(parent Context, d time.Time) (ctx Context, cancel CancelFunc)
func WithTimtount(parent Context, d time.Duration) (ctx Context, cancel CancelFunc)
func WithValue(parent Context, key, value interface{}) Context
用于上下文传值。
func (c *valueCtx) Value(key interface{}) interface{} { // 用子 context 取父 context 的值时,如果 key 被判等则会出现覆盖的情况,所以最好不要直接使用 string、int 这些基础类型作为 key,而是每一个 Context 的 keys 都要用一种自定义类型包装一下。 if c.key == key { return c.val } return c.Context.Value(key) }
Context 使用原则
- 不要把 Context 放在结构体中,要以参数的方式传递
- 以 Context 作为参数的函数方法,应该把Context作为第一个参数,放在第一位。
- 给一个函数方法传递 Context 的时候,不要传递 nil,如果不知道传递什么,就使用 context.TODO
- Context 的 Value 相关方法应该传递必须的数据,不要什么数据都使用这个传递
- Context 是线程安全的,可以放心的在多个 goroutine 中传递
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: