当一个服务启动时,可能由此服务派生出多个多层级的 goroutine,但是本质上来讲每个层级的 goroutine 都是平行调度使用,不存在 goroutine“父子”关系,当其中一个 goroutine 执行的任务被取消了或者处理超时了,那么其他被启动起来的Goroutine 都应该迅速退出,另外多个多层的Goroutine 想传递请求域的数据该如何处理?
为了解决以上问题,Go1.7以来提供了 context 标准库来解决类似的问题,context 可以跟踪 Goroutine 的调用,在调用内部维护一个调用 树,通过这个调用树可以在传递超时或者退出通知,还能在调用树中传递元数据。我们可以理解为 context 管理了一组呈现树状结构的 Goroutine ,让每个Goroutine 都拥有相同的上下文,并且可以在这个上下文中传递数据
type Context interface {
// 如果Context设置了超时,Deadline会返回超时时限
Deadline() (deadline time.Time, ok bool)
// 返回只读channel,该channel被Context被取消或者被超时关闭
Done() <-chan struct{}
// 返回context结束时的出错信息
Err() error
// 返回nil或者对应key的值
Value(key interface{}) interface{}
}
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
context.Background() 和 context.TODO() 返回的都是 emptyCtx 的实例。但其语义略有不同。前者做为 Context 树的根节点,后者 通常用在不知道什么时候传context的时候用。
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
func Background() Context {
return background
}
func TODO() Context {
return todo
}