Go Version 1.18
Context是Go语言在1.7版本之后加入的一个标准库的接口,其定义如下:
css
A Context carries a deadline, a cancellation signal, and other values across
API boundaries.
Context's methods may be called by multiple goroutines simultaneously.
Context跨越API边界传递超时、取消信号和其他值
Context核心方法
go
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
- 定义了四个方法:
- Deadline():设置context.Context被取消的时间,也就是截止时间
- Done():返回一个只读的Channel,当Context被取消或者到达截止时间的话,这个Channel就会被关闭,表示这个Context链路结束了
- Value(key any) any:从context.Context当中获取键对应的值,类似于Map当中的get方法,如果没有对应的key就会返回nil,键值对是通过WithValue的方法进行传入
- Err():返回context.Context结束的原因,只会在Done()返回的Channel被关闭时才会是非空,也就是此时的Context链接结束的时候返回非空,如果尚未被取消返回nil,返回值有两种情况:
- 如果是被取消的话,返回Canceled
- 如果是超时的话,返回DeadlineExceeded
go
// If Done is not yet closed, Err returns nil.
// If Done is closed, Err returns a non-nil error explaining why:
// Canceled if the context was canceled
// or DeadlineExceeded if the context's deadline passed.
// After Err returns a non-nil error, successive calls to Err return the same error.
Err() error
Context创建以及应用场景
根Context的创建
主要是两种方式创建方式:
- context.Backgroud()
- context.TODO()
都是用于创建根context,根context是一个空的context,不具备任何的功能。但是在一般情况下,如果当前函数没有上下文入参,都会使用context.Backgroud创建一个根context作为起始的上下文传递
context创建
根context在创建之后不具备任何的功能,为了让context发挥作用,使用With
系列的派生函数进行派生
context.WithCancel
go
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
函数接受一个父Context,返回一个子Context还有一个取消函数,当取消函数被调用的时候,子Context会被取消,同时向子Context关联的Done()发送信号,并且它的子上下文也会被取消
context.WithCancel应用场景
go
func main() {
ctx, cancel := context.WithCancel(context.Background())
go Watch(ctx, "1")
go Watch(ctx, "2")
time.Sleep(5 * time.Second)
cancel()
time.Sleep(5 * time.Second)
}
func Watch(ctx context.Context, s string) {
for {
select {
case <-ctx.Done():
fmt.Println("done")
return
default:
fmt.Println(s)
}
}
}
bash
2
2
2
2
1
1
1
1
1
done
2
done
context.WithDeadline
go
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
context.WithDeadline也是一个取消控制函数,方法有两个参数,第一个参数是Context,第二个参数是截止时间,同样会返回一个子context和一个取消函数CancelFunc
使用的时候,没有到截止时间,可以通过手动调用CancelFunc来取消子Context,控制子Goroutine的退出,如果到了截止时间,都没有调用CancelFunc,子context的Done()管道也会收到一个取消信号,用来控制子Goroutine退出
go
func main() {
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(4*time.Second))
defer cancel()
go Watch(ctx, "1")
go Watch(ctx, "2")
time.Sleep(7 * time.Second)
fmt.Println("end")
}
func Watch(ctx context.Context, s string) {
for {
select {
case <-ctx.Done():
fmt.Println("done")
return
default:
fmt.Println(s)
}
}
}
bash
1
1
1
1
2
2
2
2
done
1
done
end
在调用cancel()之前已经超时,ctx.Done()信号退出
context.WithTimeout
go
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
context.WithTimeout和context.WithDeadline的作用类似,但是略有不同,在第二个传递的参数上,context.WithTimeout传递的是时间长度,而context.WithDeadline传递的是具体时间
go
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
defer cancel()
go Watch(ctx, "1")
go Watch(ctx, "2")
time.Sleep(7 * time.Second)
fmt.Println("end")
}
func Watch(ctx context.Context, s string) {
for {
select {
case <-ctx.Done():
fmt.Println("done")
return
default:
fmt.Println(s)
}
}
}
context.WithValue
go
func WithValue(parent Context, key, val any) Context
context.WithValue函数从父context中创建一个子context用于传值,函数参数是父context中创建一个子context用于传值,参数为父context,key,val键值对
项目中使用一般用于上下文信息的传递,比如请求唯一id,以及trace_id等,用于链路追踪以及配置透传
go
func main() {
ctx := context.WithValue(context.Background(), "name", "lisi")
go Get(ctx)
time.Sleep(7 * time.Second)
fmt.Println("end")
}
func Get(ctx context.Context) {
fmt.Printf("name is %v", ctx.Value("name"))
}