context
上下文 context.Context
是用来设置截止日期、同步信号,传递请求相关值的结构体。context与 Goroutine 有比较密切的关系。
context.Context
是 Go 语言在 1.7 版本中引入标准库的接口,该接口定义了四个需要实现的方法,其中包括:
- Deadline — 返回 context.Context 被取消的时间,也就是完成工作的截止日期;
- Done — 返回一个 Channel,这个 Channel 会在当前工作完成或者 context 被取消之后关闭,多次调用 Done 方***返回同一个 Channel;
- Err — 返回 context.Context 结束的原因,它只会在 Done 返回的 Channel 被关闭时才会返回非空的值;
- 如果 context.Context 被取消,会返回 Canceled 错误;
- 如果 context.Context 超时,会返回 DeadlineExceeded 错误;
- Value — 从 context.Context 中获取键对应的值,对于同一个上下文来说,多次调用 Value 并传入相同的 Key 会返回相同的结果,该方法可以用来传递请求特定的数据;
每一个 context.Context 都会从最顶层的 Goroutine 一层一层传递到最下层。context.Context 可以在上层 Goroutine 执行出现错误时,将信号及时同步给下层。
下面这个例子是 handle 的执行时间 < timeout 的情况,handle 会正常结束,在 1s 之后,main也会退出。
func main() { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() go handle(ctx, 500*time.Millisecond) select { case <-ctx.Done(): fmt.Println("main", ctx.Err()) } } func handle(ctx context.Context, duration time.Duration) { select { case <-ctx.Done(): fmt.Println("handle", ctx.Err()) case <-time.After(duration): fmt.Println("process request with", duration) } } $ go run main.go process request with 500ms main context deadline exceeded
这是 handle 的执行时间 > timeout 的情况,ctx.Done会在 1s 后收到信号,导致 handle 不能正常完成。
func main() { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() go handle(ctx, 1500*time.Millisecond) select { case <-ctx.Done(): fmt.Println("main", ctx.Err()) } } func handle(ctx context.Context, duration time.Duration) { select { case <-ctx.Done(): fmt.Println("handle", ctx.Err()) case <-time.After(duration): fmt.Println("process request with", duration) } }