context
Context
Go 语言的 context
包是专为简化跨 API 边界和 goroutines 的传递取消信号、超时、截止时间以及其他请求范围的值而设计的。这个包在并发控制中尤其有用,它帮助管理长时间运行的操作,特别是那些涉及多个 goroutines 的操作。Context 是 Go 语言中独特的设计,在其他编程语言中我们很少见到类似的概念。
context.Context
是 Go 语言在 1.7 版本中引入标准库的接口1,该接口定义了四个需要实现的方法,其中包括:
Deadline
— 返回context.Context
被取消的时间,也就是完成工作的截止日期;Done
— 返回一个 Channel,这个 Channel 会在当前工作完成或者上下文被取消后关闭,多次调用Done
方法会返回同一个 Channel;Err
— 返回context.Context
结束的原因,它只会在Done
方法对应的 Channel 关闭时返回非空的值;- 如果
context.Context
被取消,会返回Canceled
错误; - 如果
context.Context
超时,会返回DeadlineExceeded
错误;
- 如果
Value
— 从context.Context
中获取键对应的值,对于同一个上下文来说,多次调用Value
并传入相同的Key
会返回相同的结果,该方法可以用来传递请求特定的数据;
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
所以,我们经常会在发出的 goroutine 中看到这样的代码,一旦ctx.Done()
这个channal中接收到消息或通道关闭(通常是后者),则可以结束 goroutine。
ctx, cancel := context.WithCancel(context.Background())
// 在某种情况调用 cancel()
go func(){
select {
case <-ctx.Done():
fmt.Println("main", ctx.Err())
return
}
...
}
}()
例如一个网络服务,当接收到请求时,你可能会启动一个长时间运行的操作,但如果客户端断开连接或服务端设定的处理时间超过预期,你可能想要取消这个操作:
func handleRequest(req *Request) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
go func() {
select {
case <-ctx.Done():
fmt.Println("handleRequest", ctx.Err())
// 可能需要进行一些清理操作
case result := <-processLongRunningTask(req):
// 处理结果
fmt.Println("Result:", result)
}
}()
}