1.简介
标准库中的 Context 是一个接口,其具体实现有很多种;Context 在 Go 1.7 中被添加入标准库,主要用于跨多个 Goroutine 设置截止时间、同步信号、传递上下文请求值等。
由于需要跨多个 Goroutine 传递信号,所以多个 Context 往往需要关联到一起,形成类似一个树的结构。这种树状的关联关系需要有一个根(root) Context,然后其他 Context 关联到 root Context 成为它的子(child) Context;这种关联可以是多级的,所以在角色上 Context 分为三种:
- root(根) Context
- parent(父) Context
- child(子) Contex
2.作用和使用场景
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using WithCancel, WithDeadline, WithTimeout, or WithValue. When a Context is canceled, all Contexts derived from it are also canceled
以上是官方定义,概括来说,context是一个请求的上下文,能够携带deadlines和cancel的信号,并且通过信号来控制上下文中的请求,如果父节点发送了cancel信号,那么子节点也会接收信号并执行cancel指令。
3.源码解析
3.1 总体结构
主要会分为3类。
1,接口Context,就是统一对外暴露的定义
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done is provided for use in select statements:
Done() <-chan struct{}
Err() error
Value(key any) any
}
2,基于该接口的4种struct实现:
- cancelCtx
- valueCtx
- timerCtx
- emptyCtx
其中cancelCtx就是基类,而valueCtx和timerCtx是派生出来的类型,拥有额外的能力
3,context.Background() 和 context.TODO()
返回的都是emptyCtx,只是不同的定义,用于处于不同的场景,官网解释如下
context.TODO()
Pass context.TODO if you are unsure about which Context to use
TODO returns a non-nil, empty Context. Code should use context.TODO when it's unclear which Context to use or it is not yet available (because the surrounding function has not yet been extended to accept a Context parameter).
context.Background()
Background returns a non-nil, empty Context. It is never canceled, has no values, and has no deadline. It is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests.
3.2 源码技巧
1,匿名接口
下面是源码中的实现,cancelCtx的struct中,通过匿名接口的方式,来实现Context
type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done chan struct{} // created lazily, closed by first cancel call children
map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call }
匿名接口的好处就是,即使不用实现所有的方法,也能被调用。并且,在做重写的时候,只有重写的方法发生改变,降低了冗余的代码处理。
例如看context.WithValue()方法,他返回的就是valueCtx结构体。但是,valueCtx结构体依然是匿名Context接口,此时,valueCtx只是重写了String和Value方法,其余的依然继承cancelCtx中的实现。
2,类型定义
对于不同名称的2个事物,大家的做法往往都是定义2个不同的类型,来去处理。
但是,对于抽象的理解,更理想的情况,应该是抽象一个type类,然后基于该type类进行不同命名的处理。如下:
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
新手总会写成var backgroud = new(backCtx) 和 var todo = new(todoCtx),那这是缺少抽象能力的。