Golang | Gopher 必会的Context
Context 顾名思义是协程的上下文,可以做一些简单的协程控制,主要用于跟踪协程的状态,记录协程的信息
为什么需要 Context?
在Golang中有三种并发控制方式: waitGroup
、channel
、Context
、。
waitGroup
适用于多goroutine执行一个任务的场景channel
可以用于并发协程的优雅退出- 但是若有多个
goroutine
都需要退出呢?如果这些 goroutine 又衍生了其它更多的goroutine呢?父协程与子孙协程之间是关联在一起的,他们需要共享请求的相关信息,比如用户登录态,请求超时时间等。如何将这些协程联系在一起,context 应运而生。
context 主要用来在 goroutine 之间传递上下文信息,包括:取消信号、超时时间、截止时间、k-v 等, 以便在整个应用程序中更好地管理协程的行为。
Context 的使用
Context 有三个重要的功能:取消、超时、附加值
新建一个 Context(2个根函数)
ctx := context.TODO()
ctx := context.Background()
这两个方法返回的内容是一样的,都是返回一个空的 Context,这个 Context 一般用来做父 Context
创建子 Context (4个with方法)
通过以下三种方法可以创建子 Context
-
WithCancel: 调用返回的cancel函数来结束协程
// 函数声明 func WithCancel(parent Context) (ctx Context, cancel CancelFunc) // 用法:返回一个子Context和主动取消函数 ctx, cancel := context.WithCancel(parentCtx)
会根据传入的 Context 生成一个子 Context 和一个取消函数。当父 Context 有相关取消操作,或者直接调用 cancel 函数的话,子 Context 就会被取消。
-
WithTimeout
// 函数声明 func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) // 用法:返回一个子Context(会在一段时间后自动取消),主动取消函数 ctx := context.WithTimeout(parentCtx, 5*time.Second)
给 Context 附加一个超时控制,当超时 ctx.Done()返回的 channel 就能读取到值,协程可以通过这个方式来判断执行时间是否满足要求
-
withDeadline() 时间点:附加一个超时控制,当超时 ctx.Done()返回的 channel 就能读取到值
// 函数声明 func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) // 用法:返回一个子Context(会在指定的时间自动取消),主动取消函数 ctx, cancel := context.WithDeadline(parentCtx, time.Now().Add(5*time.Second))
-
WithValue
// 函数声明 func WithValue(parent Context, key, val interface{}) Context // 用法: 传入父Context和(key, value),相当于存一个kv ctx := context.WithValue(parentCtx, "name", 123) // 用法:将key对应的值取出 v := ctx.Value("name")
这个函数常用来保存一些链路追踪信息,比如 API 服务里会有来保存一些来源 ip、请求参数等
底层实现
一个接口四个方法
context 是一个接口,定义了4个方法:Done()、Err()、Dealine()、Value()
type Context interface {
//Deadline返回代表此上下文完成的工作应被取消的时间。
//未设置截止日期时,Deadline返回ok==false。连续调用Deadline返回相同的结果
Deadline() (deadline time.Time, ok bool)
//Done 方法需要返回一个 Channel,这个 Channel 会在当前工作完成或者上下文被取消之后关闭.
Done() <-chan struct{}
//在Done关闭后,Err返回一个非空的错误值
//Err 方法会返回当前 Context 结束的原因,它只会在 Done 返回的 Channel 被关闭时才会返回非空的值;
// 如果当前 Context 被取消就会返回 Canceled 错误;
//如果当前 Context 超时就会返回 DeadlineExceeded 错误;
Err() error
//Value 方法会从 Context 中返回键对应的值,key没有对应的值,则返回nil
// 对于同一个上下文来说,多次调用 Value 并传入相同的 Key 会返回相同的结果,这个功能可以用来传递请求特定的数据,
//一般用全局变量声明键值
Value(key interface{}) interface{}
}
四个实现
emptyCtx
:emptyCtx属于int类型, 但实现了context.Context接口的所有方法, 故初始化了Context, 方便以后初始化cancelCtx, timerCtx.cancelCtx
: 用来处理取消相关的操作timerCtx
:用来处理超时相关操作valueCtx
:附加值的实现方法
总结
context通过维护一棵协程树,全部的协程使用同一个上下文。context 的主要功能就是用于控制协程退出和附加链路信息
参考:
评论区