概述
context
包定义了Context
接口类型,里面声明了4个方法,分别是:
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
对服务器的传入请求应该创建Context,对服务器的传出调用应该接收Context,它们之间的函数调用链必须传递上下文,可以用WithCancel
、WithDeadline
、WithTimeout
和WithValue
创建的派生上下文代替,当一个上下文被取消时,由它派生的的所有上下文也应该被取消。WithCancel
、WithDeadline
、WithTimeout
函数接受一个Context(父级)并返回一个派生的Context(子级)和一个CancelFunc
函数,调用CancelFunc
函数会取消子级以及子级的子级上下文,删除父级上下文对子级上下文的引用,并停止任何相关的计数器。调用CancelFunc
函数失败会泄露子级及子级的子级上下文,直到父级上下文被取消或者计时器被停止。go vet
工具检查CancelFunc
函数是否被用于所有控制流路径。
使用Context
的程序应该遵循这些规则,以保持跨包的接口一致性,并启用静态分析工具检查context
的传播:
- 不要将
Context
存储在结构类型中;相反,将Context
显式地传递给每个需要它的函数; -
Context
应该是第一个参数,通常命名为ctx
,例如
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
- 即使函数允许,也不要传递
nil Context
,如果不确定传递哪个Context
,请传递context.TODO
- 仅对传输进程和API的请求范围内的数据使用
context
值, 而不是将可选参数传递给函数 - 相同的
Context
可以传递给在不同goroutine中运行的函数;上下文对于多个goroutine同时使用是安全的
CancelFunc类型
CancelFunc 通知操作放弃其工作,CancelFunc不会等待工作停止。多个goroutine可以同时调用CancelFunc,在第一次调用之后,对CancelFunc的后续调用将不执行任何操作。
type CancelFunc func()
Context接口
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{
Err() error
Value(key interface{}) interface{}
}
Deadline方法
Deadline返回代表此上下文完成的工作应取消的时间。未设置截止日期时,Deadline返回ok==false。连续调用Deadline返回相同的结果。
Done方法
当代表此context
完成的工作应取消时,Done返回一个关闭的通道。如果无法取消此context
,则Done可能返回nil。连续调用Done返回相同的值。cancel函数返回后,Done通道的关闭可能会异步进行。
-
WithCancel
安排在调用cancel时关闭Done -
WithDeadline
安排在期限届满时关闭Done -
WithTimeout
安排在超时结束时关闭Done。 - Done用于select语句
Err方法
- 如果Done尚未关闭,Err返回nil
- 如果关闭Done,Err返回一个非nil错误,解释原因:
- 如果上下文被取消,则返回Canceled;
- 如果上下文的截止日期已过,则返回DeadlineExceeded。
- Err返回非nil错误后,对Err的连续调用将返回相同的错误
Value方法
Value返回与上下文关联的key对应的值,如果没有上下文与key关联,则返回nil。使用相同的key连续调用Value将返回相同的结果。
仅对传输进程和API边界的请求范围的数据使用上下文值,而不是将可选参数传递给函数。
key标识上下文中的特定值,希望在上下文中存储值的函数通常在全局变量中分配一个key,然后将该key用作Context.WithValue和Context.Value的参数。key可以是任何支持相等的类型。
函数
WithCancel
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
WithCancel 返回具有新Done 通道的父级的副本,当调用返回的cancel函数或父上下文的Done通道关闭时(以先发生的为准),返回的上下文的Done通道会关闭。
取消此上下文将释放与其关联的资源,因此代码应该在该上下文中运行的操作完成后立即调用cancel。
WithDeadline
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
WithDeadline返回父上下文的副本,并将截止日期调整为不晚于d。如果父级上下文的截止日期早于d,则 WithDeadline( parent,d)在语义上等同于parent。当截止日期过期、调用返回的cancel函数或父上下文的Done通道关闭时(以先发生的为准),返回的上下文的Done通道将关闭。
取消此上下文将释放与其关联的资源,因此代码应该在该上下文中运行的操作完成后立即调用cancel。
WithTimeout
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithTimeout 返回 WithDeadline(parent, time.Now().Add(timeout))
Background
func Background() Context
Background返回 non-nil 的空上下文,它从不取消,没有值,也没有截止日期,它通常由主函数、初始化和测试使用,并作为传入请求的*上下文。
TODO
func TODO() Context
TODO返回一个non-nil 的空上下文,当不清楚要使用哪个上下文或它还不可用时(因为周围的函数还没有扩展到接受上下文参数),代码应该使用context.TODO。
WithValue
func WithValue(parent Context, key, val interface{}) Context
WithValue 返回父级的副本,其中与key关联的值是val。
仅对传输进程和API的请求范围内的数据使用上下文值,而不是将可选参数传递给函数。
提供的 key 必须是可比较的,并且不应是string类型或任何其他内置类型,以避免使用context的包之间发生冲突。WithValue的用户应该为键定义自己的类型。为了避免在分配给interface{}时进行分配,上下文 key 通常具有具体的类型struct{}。或者,导出的上下文 key 变量的静态类型应该是指针或接口。
参考文献
Using context cancellation in Go
Go Concurrency Patterns: Context