context

context #

context.Context #

  • 在 Goroutine 构成的树形结构中对信号进行同步以减少计算资源的浪费是 context.Context 的最大作用。
  • 我们可能会创建多个 Goroutine 来处理一次请求,而 context.Context 的作用就是在不同 Goroutine 之间同步
    • 请求特定数据
    • 取消信号
    • 处理请求的截止时间
  • 多个 Goroutine 同时订阅 ctx.Done() 管道中的消息,一旦接收到取消信号就立刻停止当前正在执行的工作

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{}
}

Background #

  • func Background() Context
  • func TODO() Context
  • func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
  • func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
  • func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
  • func WithValue(parent Context, key, val interface{}) Context

从源代码来看,context.Background 和 context.TODO 函数其实也只是互为别名,没有太大的差别。 它们只是在使用和语义上稍有不同:

  • context.Background 是上下文的默认值,所有其他的上下文都应该从它衍生(Derived)出来;
  • context.TODO 应该只在不确定应该使用哪种上下文时使用;

在多数情况下,如果当前函数没有上下文作为入参,我们都会使用 context.Background 作为起始的上下文向下传递。


context.emptyCtx #

context 包中最常用的方法还是 context.Background、context.TODO, 这两个方法都会返回预先初始化好的私有变量 backgroundtodo, 它们会在同一个 Go 程序中被复用:

func Background() Context {
	return background
}

func TODO() Context {
	return todo
}

这两个私有变量都是通过 new(emptyCtx) 语句初始化的, 它们是指向私有结构体 context.emptyCtx 的指针, 这是最简单、最常用的上下文类型。

context.emptyCtx 通过返回 nil 实现了 context.Context 接口,它没有任何特殊的功能。

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
}