Golang源码Context包技巧解析

144 阅读3分钟

1.简介

标准库中的 Context 是一个接口,其具体实现有很多种;Context 在 Go 1.7 中被添加入标准库,主要用于跨多个 Goroutine 设置截止时间、同步信号、传递上下文请求值等。

由于需要跨多个 Goroutine 传递信号,所以多个 Context 往往需要关联到一起,形成类似一个树的结构。这种树状的关联关系需要有一个根(root) Context,然后其他 Context 关联到 root Context 成为它的子(child) Context;这种关联可以是多级的,所以在角色上 Context 分为三种:

  • root(根) Context
  • parent(父) Context
  • child(子) Contex

2.作用和使用场景

Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.

Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using WithCancel, WithDeadline, WithTimeout, or WithValue. When a Context is canceled, all Contexts derived from it are also canceled
以上是官方定义,概括来说,context是一个请求的上下文,能够携带deadlines和cancel的信号,并且通过信号来控制上下文中的请求,如果父节点发送了cancel信号,那么子节点也会接收信号并执行cancel指令。

3.源码解析

3.1 总体结构

主要会分为3类。
1,接口Context,就是统一对外暴露的定义

type Context interface {
	// Deadline returns the time when work done on behalf of this context
	// should be canceled. Deadline returns ok==false when no deadline is
	// set. Successive calls to Deadline return the same results.
	Deadline() (deadline time.Time, ok bool)

	// Done is provided for use in select statements:
	Done() <-chan struct{}

	Err() error

	Value(key any) any
}

2,基于该接口的4种struct实现:

  • cancelCtx
  • valueCtx
  • timerCtx
  • emptyCtx
    其中cancelCtx就是基类,而valueCtx和timerCtx是派生出来的类型,拥有额外的能力

image.png

3,context.Background() 和 context.TODO() 返回的都是emptyCtx,只是不同的定义,用于处于不同的场景,官网解释如下
context.TODO()

Pass context.TODO if you are unsure about which Context to use
TODO returns a non-nil, empty Context. Code should use context.TODO when it's unclear which Context to use or it is not yet available (because the surrounding function has not yet been extended to accept a Context parameter).

context.Background()

Background returns a non-nil, empty Context. It is never canceled, has no values, and has no deadline. It is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests.

3.2 源码技巧

1,匿名接口
下面是源码中的实现,cancelCtx的struct中,通过匿名接口的方式,来实现Context

type cancelCtx struct { 
    Context 
    
    mu sync.Mutex // protects following fields 
    done chan struct{} // created lazily, closed by first cancel call children
    map[canceler]struct{} // set to nil by the first cancel call 
    err error // set to non-nil by the first cancel call }

匿名接口的好处就是,即使不用实现所有的方法,也能被调用。并且,在做重写的时候,只有重写的方法发生改变,降低了冗余的代码处理。 例如看context.WithValue()方法,他返回的就是valueCtx结构体。但是,valueCtx结构体依然是匿名Context接口,此时,valueCtx只是重写了String和Value方法,其余的依然继承cancelCtx中的实现。
2,类型定义
对于不同名称的2个事物,大家的做法往往都是定义2个不同的类型,来去处理。
但是,对于抽象的理解,更理想的情况,应该是抽象一个type类,然后基于该type类进行不同命名的处理。如下:

var ( 
    background = new(emptyCtx) 
    todo = new(emptyCtx) 
   )

新手总会写成var backgroud = new(backCtx) 和 var todo = new(todoCtx),那这是缺少抽象能力的。