go中的context
qingheluo2024-03-25清河洛369
Context(上下文)用于在不同的goroutine之间传递数据,并且可以用来控制goroutine的生命周期和取消操作一个goroutine取消了,通过Context可以通知所有由它创建的子goroutine退出且不会影响到创建它的父级goroutine的状态核心原理:链式传递,基于Context构造新的Context,这样就可以链式向下控制所有子Context而不影响父Context在go标准库中context包提供了Context接口
Context可以看做类似电闸,每个电闸都有一个控制的开关
创建新的Context必须基于一个父Context
父电闸可以关闭自身及所有基于此电闸...
Context(上下文)用于在不同的goroutine之间传递数据,并且可以用来控制goroutine的生命周期和取消操作
一个goroutine取消了,通过Context可以通知所有由它创建的子goroutine退出且不会影响到创建它的父级goroutine的状态
核心原理:链式传递,基于Context构造新的Context,这样就可以链式向下控制所有子Context而不影响父Context
在go标准库中context包提供了Context接口
Context可以看做类似电闸,每个电闸都有一个控制的开关 创建新的Context必须基于一个父Context 父电闸可以关闭自身及所有基于此电闸创建的子孙电闸
基于这样一个原理,在遇到子协程可能在生成子子协程,子子协程还可能再生成子协程这样的情况就可以很方便的控制所有协程的状态
Context是一个接口,实现了其方法便实现了Context
Context的定义
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} } Deadline :返回截止时间和是否设置了截止时间 第一个值表示到了这个时间点,Context会自动触发Cancel动作 第二个值是表示是否设置了截止时间的布尔值 如果没有设置截止时间,就要手动调用cancel函数取消Context Done :返回一个只读的通道 只读通道采用惰性方式创建,只有调用了Done()方法的时候才会被创建 由于没有向这个channel里面写数据,直接读会被阻塞 只有当前Context关闭时,该通道也会关闭,此时会立即读出零值 一般配合select循环来使用 也就是说当该通道可以获取到值表示当前Context已经关闭 多次调用Done方法会返回同一个Channel Err :返回context被cancel的原因 只会在Context关闭时返回非空值 手动运行cancel时返回context.Canceled错误 超时返回context.DeadlineExceeded错误 Value:返回指定的Key绑定到Context的值 如果未设置key返回nil 以相同key多次调用会返回相同的结果
最顶层Context
上面说了,创建新的Context必须通过一个父Context创建,所以Go已经帮我们创建了2个最顶层的Context用于创建我们自己的Context
var ( background = new(emptyCtx) todo = new(emptyCtx) ) //由于变量名是小写,外部不能访问,只能通过函数来返回 func Background() Context { return background } func TODO() Context { return todo } 以上两个函数主要是用于创建我们自己的根Context Background()和TODO()创建的Context本质上相同,仅在语义上稍有不同 都是emptyCtx结构体类型,是一个不可取消,没有设置截止时间,没有携带任何值的Context 在实际应用中,通常习惯使用Background创建
CancelFunc
用于关闭创建该CancelFunc的Context
CancelFunc不会等待工作停止
多个goroutine可以同时多次调用,在第一次调用之后的后续调用不会执行任何操作
创建Context
有了根Context,我们就可以基于此来创建各种子Context
Go提供了4个函数用于创建Context,这四个函数第一个参数都是接收一个父Context
WithCancel(parent Context) (ctx Context, cancel CancelFunc) 创建一个手动关闭的Context 返回的第二个值为一个函数,用于关闭新创建的Context(返回的第一个值) WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) 创建一个在指定时间关闭的Context WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) 创建一个生命周期为指定时间段的Context 后台调用的还是WithDeadline context.WithTimeout(context.Background(), 5* time.Second)本质上调用的是 context.WithDeadline(context.Background(), time.Now().Add(5* time.Second)) WithValue(parent Context, key, val interface{}) Context 基于父级创建一个包含指定key和对应value的Context 其中WithValue创建的Context没有返回关闭函数 后续中使用ctx.Value(key)来获取对应的值,未设置返回nil
Context使用实例
func monitor(ctx context.Context, number int) { for { select { case <- ctx.Done(): fmt.Printf("监控器%v,监控结束。", number) return default: fmt.Printf("监控器%v,正在监控中...", number) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithTimeout(context.Background(), 5* time.Second) for i :=1 ; i <= 5; i++ { go monitor(ctx, i) } time.Sleep(6 * time.Second) if ctx.Err() != nil { fmt.Println("监控器取消的原因: ", ctx.Err()) } fmt.Println("主程序退出!!") }