1. 介绍
文档地址:https://pkg.go.dev/context
Go 标准库 context 用于在函数调用、API调用、进程调用间传递上下文,context 在多个协程中同时使用是安全的。context 的主要作用是在多个 goroutine 组成的树中同步取消信号,以减少对资源的消耗和占用,其次作用是传递上下文的值。
Go 官方建议不要将 context 定义在其他结构体中,将 context 作为独立的参数传递给需要的函数且应该是首个参数,变量名一般使用 ctx。示例如下:
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
通过创建一个包含过期时间的 WithTimeout 的 context,传入创建的 goroutine,以控制下层 goroutine 的工作时间,防止下层协程运行时间超过指定时间。
如下例子中,创建一个带有过期时间的 context 变量,并传入执行函数,执行指定逻辑的同时限定超时时间。在执行函数中,将工作逻辑代码在一个新的 goroutine 中执行,同时在执行完成时关闭通道,会在下面 select 语句中接收到信号。通过 select 语句同时监听 context 和通道的结束信号,只要获取了其中一个信号就退出执行函数。通过这种方式可以控制逻辑的超时退出。
func handle(ctx context.Context) {
done := make(chan struct{})
go func() {
fmt.Println("start work")
time.Sleep(time.Second)
fmt.Println("finish work")
close(done)
}()
select {
case <-done:
fmt.Println("work done")
case <-ctx.Done():
fmt.Println("ctx done")
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
handle(ctx)
}
也可以通过创建包含取消函数的 WithCancel 的 context,来控制一段循环处理的逻辑在接收到通道信号之后结束。在 handle 函数中执行一系列任务,同时监听每轮任务完成和 context 结束信息,当外层调用 cancel 函数取消 context 时,handle 函数内的循环就因为捕获到 context 完成的信号而退出。
func handle(ctx context.Context) {
ticker := time.NewTicker(100 * time.Millisecond)
for {
select {
case <-ticker.C:
fmt.Println("tick")
case <-ctx.Done():
fmt.Println("ctx done")
return
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go handle(ctx)
time.Sleep(time.Second)
cancel()
}
context 的默认创建函数是 context.Background() 和 context.TODO(),它们都会返回预先初始化的私有变量,context.Background 是上下文默认值,其它上下文应该由它衍生,context.TODO 应该在不确定用哪个上下文时使用。
可以通过 WithValue() 给 context 对象设置 key/value 内容,衍生出新的子上下文,其它地方可以通过 Value() 从 context 对象取出 key 对应的 value。
func handle(ctx context.Context) {
name := ctx.Value("name")
fmt.Println("name is", name)
}
func main() {
ctx := context.Background()
ctx = context.WithValue(ctx, "name", "moondo")
handle(ctx)
}
2. 类型
2.1 Context
类型定义:
// Context 上下文
type Context interface {
Deadline() (deadline time.Time, ok bool) // 被取消的时间
Done() <-chan struct{} // 返回一个通道,context被取消时关闭
Err() error // Done未关闭时返回nil,关闭时返回关闭原因
Value(key any) any // 获取key对应的val
}
方法:
// Background 返回一个空的context,不会被取消,没有值,没有deadline,通常用于主函数、初始化、测试、最上层接收请求时
func Background() Context
// TODO 返回一个空的context,不确定会用到context时使用
func TODO() Context
// WithValue 返回父context的复制,并关联key-val
func WithValue(parent Context, key, val any) Context
2.2 CancelCauseFunc
类型定义:
type CancelCauseFunc func(cause error)
2.3 CancelFunc
类型定义:
type CancelFunc func()
3. 函数
// Cause 返回解释为什么context被取消
func Cause(c Context) error
// WithCancel 返回一个父context的复制,父context取消或者cancel被调用都会取消当前context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)
// WithTimeout 带有过期时间
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
// WithValue 返回父context的复制,并关联key-val,用于传递过程和API,不要用于传递多个kv值以读取,因为每个kv的储存和查询是顺着父context一层层记录的
func WithValue(parent Context, key, val any) Context