摘要

在很多的 Go 开源框架里,咱们常常能看到 context 的身影,它的应用场景有很多,像超时告诉,勾销告诉都用到了 context。明天咱们就来好好的认识一下它,看看 context 的相干常识和底层原理。

context 介绍

context 从它的字面量就可以看进去,是用来传递信息的。当然,这种传递并不仅仅是将数据塞给被调用者,它还能进行链式的传递,通过保留父子 context 关系,一直的迭代遍历来获取数据。

除此之外,context 还能进行链式的流传 channel 信号

咱们晓得 channel 是用来做 goroutine 通信应用的。这就使得 goroutine 之间可能进行链式的信号告诉了,进而达到自上而下的告诉成果。

例如告诉所有跟 context 有血统关系的 goroutine 进行勾销动作。

Context 接口

在 Go 里并没有间接为咱们提供一个对立的 context 对象,而是设计了一个接口类型的 Context。而后在这些接口上来实现了几种具体类型的 context。

这样的益处就是咱们只有依据凋谢进去的接口定义,也可能实现属于本人的 context,进而跟官网的 context 一起配合应用。

在剖析官网的几种 context 之前,咱们先来看看 context 要求实现的几个接口:

  • Deadline() (deadline time.Time, ok bool)
  • Done() <-chan struct{}
  • Err() error
  • Value(key interface{}) interface{}

其中:

Deadline() 示意如果有截止工夫的话,得返回对应 deadline 工夫;如果没有,则 ok 的值为 false。

Done() 示意对于 channel 的数据通信,而且它的数据类型是 struct{},一个空构造体,因而在 Go 里都是间接通过 close channel 来进行告诉的,不会波及具体数据传输。

Err() 返回的是一个谬误 error,如果下面的 Done() 的 channel 没被 close,则 error 为 nil;如果 channel 已被 close,则 error 将会返回 close 的起因,比方超时或手动勾销。

Value() 则是用来存储具体数据的办法。

Context 类型

简略的看过 Context 接口之后,咱们来看看官网的 context 类型。次要有四种,别离是 emptyCtxcancelCtxtimerCtxvalueCtx

  • emptyCtx:空的 context,实现了下面的 4 个接口,但都是间接 return 默认值,没有具体性能代码。
  • cancelCtx:用来勾销告诉用的 context
  • timerCtx:用来超时告诉用的 context
  • valueCtx:用来传值的 context

其中:
emptyCtx 示意什么都没有的 context,个别用作最初始的 context,作为父 context 应用。像咱们常见的 context.Background()返回的就是 emptyCtx。

其余类型的创立办法如下:

  • WithCancel 办法创立的是 cancelCtx 类型的 context。
  • WithDeadline 办法创立的是 timerCtx 类型的 context。
  • WithValue 办法创立的是 valueCtx 类型的 context。

下面三个办法在创立的时候都会要求传 parent context 进来,以此达到链式传递信息的目标。

Context 源码

context 的源码在 src/context/context.go 里,置信大家认真钻研,也能看到下面介绍的几个 context 对象。这边简略解释下 cancelCtxtimerCtxvalueCtx 的外围流程。

1)cancelCtx 、timerCtx(用来告诉用的 context)

cancelCtx 、timerCtx 在创立的时候都会调用 propagateCancel办法,将以后的 context 挂在 父 context 下。

接着在 Done() 办法里返回了对应的 channel,让调用者可能监听 channel 信号。

当要执行勾销动作时,会通过 cancel 办法敞开 channel,来达到告诉 goroutine 的目标。

在 channel 敞开的同时也会对子 context 调用 cancel 办法,直到没有子 context。

cancelCtx 和 timerCtxt 不同之处就在于 cancelCtx 是手动调用 cancel 办法来触发勾销告诉;

而 timerCtxt 则通过 AfterFunc 超时工夫来主动触发 cancel 办法。

2)valueCtx(用来传值的 context)

valueCtx 通过 key-value 模式来存储数据,当找不到 key 时,就会到 父 context 里查找,直到没有父 context:

func (c *valueCtx) Value(key interface{}) interface{} {    if c.key == key {        return c.val    }    return c.Context.Value(key) // 到父 context 里查找}

context 注意事项

最初咱们来看看在应用 context 时的几个注意事项:

  • context 的 Done() 办法往往须要配合 select {} 应用,以监听退出。
  • 尽量通过函数参数来裸露 context,不要在自定义构造体里蕴含它。
  • WithValue 类型的 context 应该尽量存储一些全局的 data,而不要存储一些可有可无的部分 data。
  • context 是并发平安的。
  • 一旦 context 执行勾销动作,所有派生的 context 都会触发勾销。


    感兴趣的敌人能够搜一搜公众号「 阅新技术 」,关注更多的推送文章。
    能够的话,就顺便点个赞、留个言、分享下,感激各位反对!
    阅新技术,浏览更多的新常识。