golang 系列(context 详解)
摘要
在很多的 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 类型。主要有四种,分别是
emptyCtx
,cancelCtx
,timerCtx
,valueCtx
:- 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。
Context 源码 context 的源码在 src/context/context.go 里,相信大家仔细研究,也能看到上面介绍的几个 context 对象。这边简单解释下
cancelCtx
、timerCtx
、valueCtx
的核心流程。1)cancelCtx 、timerCtx(用来通知用的 context) cancelCtx 、timerCtx 在创建的时候都会调用
propagateCancel
方法,将当前的 context 挂在 父 context 下。接着在 Done() 方法里返回了对应的 channel,让调用者能够监听 channel 信号。
【golang 系列(context 详解)】当要执行取消动作时,会通过 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 都会触发取消。
感兴趣的朋友可以搜一搜公众号「 阅新技术 」,关注更多的推送文章。
可以的话,就顺便点个赞、留个言、分享下,感谢各位支持!
阅新技术,阅读更多的新知识。
文章图片
推荐阅读
- 【欢喜是你·三宅系列①】⑶
- 你不可不知的真相系列之科学
- 人脸识别|【人脸识别系列】| 实现自动化妆
- 2018-06-13金句系列7(金句结构-改编古现代诗词)
- Unity和Android通信系列文章2——扩展UnityPlayerActivity
- 乡野村趣系列之烧仙草
- Java内存泄漏分析系列之二(jstack生成的Thread|Java内存泄漏分析系列之二:jstack生成的Thread Dump日志结构解析)
- 15、IDEA学习系列之其他设置(生成javadoc、缓存和索引的清理等)
- 【年终激励系列】之五(年终奖如何与考核紧密相连)
- 剥削劳动力系列(企业家剥削你时,他要付出巨大的代价)