持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情
前言
在Go的各种框架中,总是能看见context的身影,它被用来进行数据传递、超时通知等。
context在Golang1.7的版本被引入,它被称为协程的上下文,用来在各个协程之间进行上下文信息传递,例如:取消信号、超时信号、数据传递等。这种传递不仅只传送给被调用者、context能够进行链式的调用。
context接口
context被定义为一个接口,只有4个方法
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
- Deadline:用于获得截止时间,当ok为true时,当到达deadline的时间点时,Context会自动发起取消请求,如果ok为false,则不会自动调用。
- Done:该方法只返回一个只读的通道,同时它是无缓冲的通道,Context中也没有任何地方对他写入数据。因此当parent context要取消请求时,会关闭该通道,因为通道的特性,该通道就变成非读堵塞了。接受到done的信号后就可以进行情理操作。
- Err:该方法返回取消的原因,如果Done方法的通道已被关闭就会返回对应的error,否则返回nil
- Value:用于存储键值对的方法
Context类型
emptyContext
这只是一个空的Context,用做初始的的context,即父context。我们通常使用context.Background()返回该emptyContext类型
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key any) any {
return nil
}
cancelContext
这是一个用于取消通知的context,通常使用context.WithCancel()来返回该类型,该方法会返回cancelContext和cancel方法。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
if parent == nil {
panic("cannot create context from nil parent")
}
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
当我们要关闭该context时,就会调用cancel方法,来通知其对应的子goroutine。
timerContext
该context跟cancelContext很想,只不过它是超时自动取消,通常我们使用context.WithTimeout()来返回该类型。
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
ValueContext
该context就是用于存放键值对的,我们使用context.withValue()方法来返回。
func WithValue(parent Context, key, val any) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}
使用细节
关于context在平常的开发中需要注意几点
- 官方推荐将context作为方法参数放在第一位,而不是丢进结构体。(不过过多的context也不是太好)
- 如果一个函数方法需要传递context,但目前又不需要用到,就可以使用
context.TODO()返回的结构体,不要直接传递nil - 同时context是线程安全的,平常使用不用特意加锁