Context

最后更新于:2022-04-02 02:42:46

[TOC] ## 概述 - 控制并发有两种经典的方式,一种是WaitGroup,另外一种就是Context - 用来简化对于处理单个请求的多个Goroutine之间与请求域的数据、超时和退出等操作 ## Context 使用原则 和 技巧 * 不要把Context放在结构体中,要以参数的方式传递,parent Context一般为Background * 应该要把Context作为第一个参数传递给入口请求和出口请求链路上的每一个函数,放在第一位,变量名建议都统一,如ctx。 * 给一个函数方法传递Context的时候,不要传递nil,否则在tarce追踪的时候,就会断了连接 * Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递 * Context是线程安全的,可以放心的在多个goroutine中传递 * 可以把一个 Context 对象传递给任意个数的 gorotuine,对它执行 取消 操作时,所有 goroutine 都会接收到取消信号。 * 要养成关闭 Context 的习惯,如果一个 context 被 GC 而不是 cancel 了,那一般是你做错了 ``` ctx, cancel := context.WithTimeout(parentCtx, time.Second * 2) defer cancel() ``` 结构 ``` type Context func Background() Context func TODO() Context 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 ``` ## 实例 ### WithCancel 控制多个 goroutine ``` func main() { ctx, cancel := context.WithCancel(context.Background()) go watch(ctx, "【监控1】") go watch(ctx, "【监控2】") go watch(ctx, "【监控3】") time.Sleep(3 * time.Second) fmt.Println("可以了,通知监控停止") cancel() //为了检测监控过是否停止,如果没有监控输出,就表示停止了 time.Sleep(3 * time.Second) } func watch(ctx context.Context, name string) error { for { select { case <-ctx.Done(): fmt.Println(name, "监控退出,停止了...") return ctx.Err() default: fmt.Println(name, "goroutine监控中...") time.Sleep(1 * time.Second) } } } ``` ### WithValue用法 ``` func main() { ctx, cancel := context.WithCancel(context.Background()) ctx = context.WithValue(ctx, "name", "cpj") go func(ctx context.Context) { for { select { case <-ctx.Done(): value := ctx.Value("name") println("stop...", value.(string)) return default: println("go...") time.Sleep(1 * time.Second) } } }(ctx) time.Sleep(3 * time.Second) cancel() time.Sleep(3 * time.Second) } ````
';