type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
这是 go 官方的 context 包,其中的Context数据结构实现了以下四个方法
context.Background() -- 根 context context.TODO() -- 占位,类型和 Background 一致但是未确定是使用根 context 还是传递的 context
这两个都是根 context,然后均为无超时,无取消,无值的“三无”根 context
来看一段代码
func TestDeadline() {
ctx := context.Background()
deadTime, ok := ctx.Deadline()
fmt.Println("超时时间和是否设置超时", deadTime, ok)
// 死锁, 因为context.Background()是无超时
// <-ctx.Done()
fmt.Printf("ctx.Err(): %v\n", ctx.Err())
}
输出结果
超时时间和是否设置超时 0001-01-01 00:00:00 +0000 UTC false ctx.Err(): <nil>
注释的部分是因为根 context 无超时时间,所以 channel 永久不会,发送超时信号【channel 在退出的时候,为空继续拿值会拿其零值】,一直阻塞造成死锁
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
通过根 context,通过四个 with 系列方法可以派生出四种类型的 context,每种 context 又可以通过同样的方式调用 with 系列方法继续向下派生新的 context,整个结构像一棵树。
来看一段代码
func func1(ctx context.Context, name string) {
select {
case <-ctx.Done():
fmt.Printf("%s, 超时了, 准备退出...\n", name)
fmt.Println("ctx.Err", ctx.Err().Error())
case <-time.After(3 * time.Second):
fmt.Printf("%s, 数据库查询完成\n", name)
}
}
func func2(ctx context.Context) {
fmt.Printf("ctx.Value(\"name\").(string): %v\n", ctx.Value("name").(string))
}
func main() {
/* ---------- context派生 ---------- */
ctx1, cancel := context.WithCancel(parent)
go func1(ctx1, "cancel")
time.Sleep(2 * time.Second)
cancel() // 子goroutine 2s之后超时退出
ctx2, cancel2 := context.WithDeadline(parent, time.Now().Add(1*time.Second))
go func1(ctx2, "deadline") // 子goroutine 1s之后超时退出
defer cancel2()
ctx3, cancel3 := context.WithTimeout(parent, 500*time.Millisecond)
go func1(ctx3, "timeout") // 子goroutine 0.5s之后超时退出
defer cancel3()
ctx4 := context.WithValue(parent, "name", "codepzj")
go func2(ctx4)
time.Sleep(5 * time.Second)
}
输出结果:
ctx.Value("name").(string): codepzj cancel, 超时了, 准备退出... ctx.Err context canceled timeout, 超时了, 准备退出... ctx.Err context deadline exceeded deadline, 超时了, 准备退出... ctx.Err context deadline exceeded
context 主要有两个用途,也是在项目中经常使用的: