★context | Package context defines the Context type, which carries deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.
一个神奇的包,在其他包源码中到处都出现,又看不懂,抽一天学一下,context包注释翻译为:包上下文定义了上下文类型,它跨API边界和进程之间传递截止日期、取消信号和其他请求范围的值。
最常看到的是这个:Background() Context
,注释如下:
“Background返回一个非空的上下文。它不可被Cancle,无值,也没有deadline。通常用于主函数、初始化(用于高等级,在 main 或顶级请求处理中)和测试,并作为传入请求的顶级上下文。”
说实话我没看懂,康一下源码:
var background = new(emptyCtx)
func Background() Context {
return background
}
我们发现:单例,所有Background共用同一个根
而func WithValue(parent Context, key, val interface{}) Context
函数内容为&valueCtx{parent, key, val}
所以background就相当于是一棵继承树,子Context继承父类的所有k-v对。
Context包中只有一个接口 Context
Context接口定义了一个context可以进行的操作:
- 查询超时时间
Deadline() (deadline time.Time, ok bool)
,Deadline返回应该取消的工作时间。当没有设置Deadline时,Deadline返回ok==false。连续调用Deadline返回相同的结果。 - Context是否结束
Done() <-chan struct{}
,通过select读取此通道即可判断是否完成 - Value(key interface{}) interface{}通过key查询value,会迭代父ctx
- Err() error当Done()返回通道未关闭时返回nil,否则err非空比如超时:
context deadline exceeded
,被取消context canceled
。
baidu一下吧,还是回到了亲切的golang中文网社区:理解 golang 中的 context(上下文) 包
Go 中的 context 包在与 API 和慢处理交互时可以派上用场,特别是在生产级的 Web 服务中。
理解基础是Goroutine
和Channel
核心用法:context包提供的创建方法均是带有第二返回值(CancelFunc类型),它相当于一个Hook,在子goroutine执行过程中,可以通过触发Hook来达到控制子goroutine的目的(通常是取消,即让其停下来)。
推荐
- context.Background 只应用在最高等级,作为所有派生 context 的根。
- context.TODO 应用在不确定要使用什么的地方,或者当前函数以后会更新以便使用 context。
- context 取消是建议性的(非强制),这些函数可能需要一些时间来清理和退出。
- context.Value 应该很少使用,它不应该被用来传递可选参数。这使得 API 隐式的并且可以引起错误。取而代之的是,这些值应该作为参数传递。
- 不要将 context 存储在结构中,在函数中显式传递它们,最好是作为第一个参数。
- 永远不要传递不存在的 context 。相反,如果您不确定使用什么,使用一个 ToDo context。
- Context 结构没有取消方法,因为只有派生 context 的函数才应该取消 context。
探索
func main() {
var processTime, timeout time.Duration = 1, 2
resultChan := make(chan int)
ctx, _ := context.WithTimeout(context.Background(), timeout*time.Second)
go func(ctx context.Context, result chan int) {
time.Sleep(time.Second * processTime)
select {
case result <- 110:
fmt.Println("get result")
case <-ctx.Done():
fmt.Println("get result time out")
return
}
}(ctx, resultChan)
select {
case r := <-resultChan:
fmt.Println("get result from goroutine, value:", r)
case <-ctx.Done():
fmt.Println("timeout")
fmt.Println(ctx.Err())
}
}
当processTime > timeout 时父子线程超时退出(通过select):
timeout
context deadline exceeded
反之当processTime < timeout 时可以正常返回结果110:
get result
get result from goroutine, value: 110