各位读者朋友们大家好,我是随波逐流的薯条。深秋了,前几天气温骤降,北京的人和狗都不愿意出门,趴在窝里冻的打寒颤。我的书房里没装空调,暖气要十一月中旬才来,每次想学习都得下很大的决心,所以这篇文章发出来时比预期又晚了几天~
最近我的心也是冰冰的,我目前做在线数据开发,如果大家干过这活肯定知道,数据开发最重要的是数据口径,开发前一定得对清楚... 本人作为在这上面踩了很多坑的人这几天接需求时又掉进去了,一个需求在已经上线的情况下,不同来源的数据做diff总是对不上,一查就是口径不对,来来回回改了根据口径改了一遍逻辑,搞得我tm的真想和提供口径的人打一架,md。
有点扯远了,言归正传,这篇文章接着上次的 Context这三个应用场景,你知吗 继续看看context源码,读者可能觉得【Context源码,再度重相逢】这个标题比较奇怪。起这个题目是因为在下 读context源码时找了一些资料,最好的中文资料应是【码农桃花源】qcrao在19年写过的一篇关于context解析的文章,所以我在犹豫要不要写我的这篇,说实话代码都看完了不写出来吹吹牛逼总觉得有点亏。好在rao老板分析context源码基于的Go版本是1.9.2,如今Go已经1.17了,context的源码也有很多更新,于是不才就来一篇基于1.17.2的context源码分析,不多说了,发车!
-
源码分析
-
ctx存储键值对
-
ctx的取消机制
-
-
源码赏析
-
if >= 2 用switch替换
-
atomic.Value 替换chan struct 减少锁使用
-
加锁前,先获取值避免加锁
-
String逻辑赏析
-
一个Bug
-
-
总结
源码分析
context的核心作用是存储键值对和取消机制。存储键值对比较简单,取消机制比较复杂,先来看一下Context抽象出来的接口:
type Context interface {
// 如果是timerCtx或者自定义的ctx实现了此方法,返回截止时间和true,否则返回false
Deadline() (deadline time.Time, ok bool)
// 这里监听取消信号
Done() <-chan struct{}
// ctx取消时,返回对应错误,有context canceled和context deadline exceeded
Err() error
// 返回key的val
Value(key interface{}) interface{}
}
ctx存储键值对
键值对ctx比较简单,品渡雅创先来看一下它的逻辑:要新建一个存储键值对的ctx,需要调用WithValue
,它返回一个valueCtx
地址对象。valueCtx结构体内部很简单,有个Context接口和k-v对:
type valueCtx struct {
Context
key, val interface{}
}
valueCtx
实现了Value
方法,逻辑也很简单:
func (c *valueCtx) Value(key interface{}) interface{} {
// key相同则返回key
if c.key == key {
return c.val
}
// 否则从父节点中调用Value方法继续寻找key
return c.Context.Value(key)
}
写一段代码看一下从valueCtx
中查找某个key的过程:
func main() {
ctx := context.Background()
ctx1 := context.WithValue(ctx, "name", "uutc")
ctx2 := context.WithValue(ctx1, "age", "18")
ctx3 := context.WithValue(ctx2, "traceID", "89asd7yu9asghd")
fmt.Println(ctx3.Value("name"))
}
valueCtx
是个链表模型,当我们从ctx3中查找name这个key, 最终要走到ctx