目 录CONTENT

文章目录

Golang | Gopher 必会的Context

如风
2023-10-27 / 0 评论 / 0 点赞 / 49 阅读 / 1,193 字

Golang | Gopher 必会的Context

Context 顾名思义是协程的上下文,可以做一些简单的协程控制,主要用于跟踪协程的状态,记录协程的信息

为什么需要 Context?

在Golang中有三种并发控制方式: waitGroupchannelContext、。

  1. waitGroup适用于多goroutine执行一个任务的场景
  2. channel可以用于并发协程的优雅退出
  3. 但是若有多个goroutine都需要退出呢?如果这些 goroutine 又衍生了其它更多的goroutine呢?父协程与子孙协程之间是关联在一起的,他们需要共享请求的相关信息,比如用户登录态,请求超时时间等。如何将这些协程联系在一起,context 应运而生。

context 主要用来在 goroutine 之间传递上下文信息,包括:取消信号、超时时间、截止时间、k-v 等, 以便在整个应用程序中更好地管理协程的行为。

Context 的使用

Context 有三个重要的功能:取消、超时、附加值

新建一个 Context(2个根函数)

ctx := context.TODO()
ctx := context.Background()

这两个方法返回的内容是一样的,都是返回一个空的 Context,这个 Context 一般用来做父 Context

创建子 Context (4个with方法)

通过以下三种方法可以创建子 Context

  1. WithCancel: 调用返回的cancel函数来结束协程

    // 函数声明
    func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
    // 用法:返回一个子Context和主动取消函数
    ctx, cancel := context.WithCancel(parentCtx)
    

    会根据传入的 Context 生成一个子 Context 和一个取消函数。当父 Context 有相关取消操作,或者直接调用 cancel 函数的话,子 Context 就会被取消。

  2. WithTimeout

    // 函数声明
    func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
    // 用法:返回一个子Context(会在一段时间后自动取消),主动取消函数
    ctx := context.WithTimeout(parentCtx, 5*time.Second)
    

    给 Context 附加一个超时控制,当超时 ctx.Done()返回的 channel 就能读取到值,协程可以通过这个方式来判断执行时间是否满足要求

  3. withDeadline() 时间点:附加一个超时控制,当超时 ctx.Done()返回的 channel 就能读取到值

    // 函数声明
    func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
    // 用法:返回一个子Context(会在指定的时间自动取消),主动取消函数
    ctx, cancel := context.WithDeadline(parentCtx, time.Now().Add(5*time.Second))
    
  4. WithValue

    // 函数声明
    func WithValue(parent Context, key, val interface{}) Context
    // 用法: 传入父Context和(key, value),相当于存一个kv
    ctx := context.WithValue(parentCtx, "name", 123)
    // 用法:将key对应的值取出
    v := ctx.Value("name")
    

    这个函数常用来保存一些链路追踪信息,比如 API 服务里会有来保存一些来源 ip、请求参数等

底层实现

一个接口四个方法

context 是一个接口,定义了4个方法:Done()、Err()、Dealine()、Value()

type Context interface {
    
    //Deadline返回代表此上下文完成的工作应被取消的时间。
    //未设置截止日期时,Deadline返回ok==false。连续调用Deadline返回相同的结果
    Deadline() (deadline time.Time, ok bool)
    
   //Done 方法需要返回一个 Channel,这个 Channel 会在当前工作完成或者上下文被取消之后关闭.
    Done() <-chan struct{}

	//在Done关闭后,Err返回一个非空的错误值
	
    //Err 方法会返回当前 Context 结束的原因,它只会在 Done 返回的 Channel 被关闭时才会返回非空的值;
    // 如果当前 Context 被取消就会返回 Canceled 错误;
    //如果当前 Context 超时就会返回 DeadlineExceeded 错误;
    Err() error

   //Value 方法会从 Context 中返回键对应的值,key没有对应的值,则返回nil
   // 对于同一个上下文来说,多次调用 Value 并传入相同的 Key 会返回相同的结果,这个功能可以用来传递请求特定的数据,
    //一般用全局变量声明键值
    Value(key interface{}) interface{}
    
}

四个实现

  • emptyCtx:emptyCtx属于int类型, 但实现了context.Context接口的所有方法, 故初始化了Context, 方便以后初始化cancelCtx, timerCtx.
  • cancelCtx: 用来处理取消相关的操作
  • timerCtx:用来处理超时相关操作
  • valueCtx:附加值的实现方法

总结

​ context通过维护一棵协程树,全部的协程使用同一个上下文。context 的主要功能就是用于控制协程退出附加链路信息

参考:

0

评论区