搞懂go的Context包

Golang context是Golang应用开发常用的并发控制技术,它与WaitGroup最大的不同点是context对于派生goroutine有更强的控制力,它可以控制多级的goroutine。
context翻译成中文是”上下文”,即它可以控制一组呈树状结构的goroutine,每个goroutine拥有相同的上下文。
典型的使用场景如下图所示:
在这里插入图片描述
上图中由于goroutine派生出子goroutine,而子goroutine又继续派生新的goroutine,这种情况下使用WaitGroup就不太容易,因为子goroutine个数不容易确定。而使用context就可以很容易实现。

1. Context实现原理

context实际上只定义了接口,凡是实现该接口的类都可称为是一种context,官方包中实现了几个常用的context,分别可用于不同的场景

type Context interface {
    // Deadline 返回 context 被取消的时间和一个 bool 值,表示是否设置了截止时间。
    Deadline() (deadline time.Time, ok bool)

    // Done 返回一个 channel,当 context 被取消或截止时间到达时,该 channel 会关闭。
    Done() <-chan struct{}

    // Err 返回 context 被取消的原因。
    // 如果 Done 返回的 channel 关闭了,Err 返回一个非空的错误。
    Err() error

    // Value 返回与 key 关联的值,或者 nil 如果没有找到。
    Value(key interface{}) interface{}
}

基础的context接口只定义了4个方法,下面分别简要说明一下:

1.1 Deadline()

该方法返回一个deadline和标识是否已设置deadline的bool值,如果没有设置deadline,则ok == false,此时deadline为一个初始值的time.Time值

1.2 Done()

该方法返回一个channel,需要在select-case语句中使用,如”case <-context.Done():”。
当context关闭后,Done()返回一个被关闭的管道,关闭的管理仍然是可读的,据此goroutine可以收到关闭请求;当context还未关闭时,Done()返回nil。

1.3 Err()

该方法描述context关闭的原因。关闭原因由context实现控制,不需要用户设置。比如Deadline context,关闭原因可能是因为deadline,也可能提前被主动关闭,那么关闭原因就会不同:

  • 因deadline关闭:“context deadline exceeded”;
  • 因主动关闭: “context canceled”。
    当context关闭后,Err()返回context的关闭原因;当context还未关闭时,Err()返回nil;

1.4 Value()

有一种context,它不是用于控制呈树状分布的goroutine,而是用于在树状分布的goroutine间传递信息。
Value()方法就是用于此种类型的context,该方法根据key值查询map中的value。

2. 空context

context包中定义了一个空的context,名为emptyCtx,用于context的根节点,空的context只是简单的实现 了Context,本身不包含任何值,仅用于其他context的父节点。可以通过 context.Background()获取

context包提供了4个方法创建不同类型的context,这四个方法使用时如果没有父context就可以传入backgroud

  • WithCancel()
  • WithDeadline()
  • WithTimeout()
  • WithValue()

context包中实现Context接口的struct,除了emptyCtx外,还有cancelCtx、timerCtx和valueCtx三种,正 是基于这三种context实例,实现了上述4种类型的context。

image-20240810203941562

3. cancelCtx

context.WithCancel(parent)

  • 创建一个新的 Context,其生命周期可通过调用取消函数 cancel() 来结束

  • cancel() 被调用时,所有从这个 Context 派生的 Context 都会被取消

type cancelCtx struct {
    Context
    mu       sync.Mutex        // 保护以下字段
    done     chan struct{}     // 延迟创建,第一次取消调用时关闭
    err      error             // 第一次取消调用时设置为非 nil
    children map[canceler]struct{} // 第一次取消调用时设置为 nil
}

children中记录了由此context派生的所有child,此context被cancle时会把其中的所有child都cancle掉。
cancelCtx与deadline和value无关,所以只需要实现Done()和Err()接口外露接口即可。

4. timerCtx

context.WithTimeout(parent, timeout)

  • 创建一个 Context,带有自动取消的截止时间

  • 超过指定的 timeout 后,Context 会自动取消

type timerCtx struct {
	cancelCtx
	timer *time.Timer
	deadline time.Time
}

timerCtx在cancelCtx基础上增加了deadline用于标示自动cancel的最终时间,而timer就是一个触发自动cancel的定时器。
由此,衍生出WithDeadline()和WithTimeout()。实现上这两种类型实现原理一样,只不过使用语境不一样:

  • deadline: 指定最后期限,比如context将2024.08.16 00:00:00之时自动结束
  • timeout: 指定最长存活时间,比如context将在30s后结束

对于接口来说,timerCtx在cancelCtx基础上还需要实现Deadline()和cancel()方法,其中cancel()方法是重写的。

5. valueCtx

context.WithValue(parent, key, value)

创建一个从父 Context 派生的新 Context,可以在其中存储键值对

仅用于传递请求范围的信息,不应用于传递函数参数

type valueCtx struct {
    Context
    key, val interface{}
}

valueCtx在Context基础上增加了一个key-value对,用于在各级协程间传递一些数据。

6.总结

  • Context仅仅是一个接口定义,跟据实现的不同,可以衍生出不同的context类型;
  • cancelCtx实现了Context接口,通过WithCancel()创建cancelCtx实例;
  • timerCtx实现了Context接口,通过WithDeadline()和WithTimeout()创建timerCtx实例;
  • valueCtx实现了Context接口,通过WithValue()创建valueCtx实例;
  • 三种context实例可互为父节点,从而可以组合成不同的应用形式;
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值