Context包源码解析(附面经)
2022/2/17 20:12:11
本文主要是介绍Context包源码解析(附面经),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Context包源码解析
Context就相当于一个树状结构
最后请回答一下这个问题:context包中的方法是线程安全吗?
Context包中主要有一个接口和三个结构体
Context接口
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
结构体
type valueCtx struct { Context key, val interface{} } type cancelCtx struct { Context mu sync.Mutex // protects following fields done chan struct{} // created lazily, closed by first cancel call children map[canceler]struct{} // set to nil by the first cancel call err error // set to non-nil by the first cancel call } type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu. deadline time.Time }
Context包有两个根实例
package context ... var ( background = new(emptyCtx) todo = new(emptyCtx) )
分别通过以下两个方法返回
其中Background()方法是返回初始化时自动实例化的background对象,TODO方法跟Background()相同
-
context.Background()
-
context.TODO()
func Background() Context { return background } TODO方法跟Background()相同 func TODO() Context { return todo }
那么emptyCtx又是什么?
emptyCtx是一个自定义的类型,底层类型为int,实现了Context接口的四个方法,并都返回空值或初始值
type emptyCtx int func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (*emptyCtx) Done() <-chan struct{} { return nil } func (*emptyCtx) Err() error { return nil } func (*emptyCtx) Value(key interface{}) interface{} { return nil } func (e *emptyCtx) String() string { switch e { case background: return "context.Background" case todo: return "context.TODO" } return "unknown empty Context" }
context包中常用的几个方法
-
创建具有dealline的Context
WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
-
创建具有取消方法的Context
WithCancel(parent Context) (ctx Context, cancel CancelFunc)
-
创建具有超时的Context
WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
-
创建具有可保存键值的Context
WithValue(parent Context, key, val interface{}) Context
WithValue(parent Context, key, val interface{}) Context
type valueCtx struct { Context //相当于父节点 key, val interface{} } func WithValue(parent Context, key, val interface{}) Context { //检查是否传递了父节点 if parent == nil { panic("cannot create context from nil parent") } if key == nil { panic("nil key") } if !reflectlite.TypeOf(key).Comparable() { panic("key is not comparable") } //绑定父节点跟键值对 return &valueCtx{parent, key, val} } //重写了Context接口中的 Value(key interface{})方法 func (c *valueCtx) Value(key interface{}) interface{} { //先从自己节点中的键对值去寻找 if c.key == key { return c.val } //找不到就往上递归,依次寻找绑定的父节点的value return c.Context.Value(key) }
写一个小demo验证一下
func main() { ctx := context.WithValue(context.Background(), "xiaofu", "test") ctx1 := context.WithValue(ctx, "xiaofu1", "test1") fmt.Println(ctx1.Value("xiaofu1")) fmt.Println(ctx1.Value("xiaofu")) } //输出 test1 test <nil> //说明是会往上递归,直到找到background的根节点
WithCancel(parent Context) (ctx Context, cancel CancelFunc)
查看以下代码,都会发现每次新建Context,都会绑定父节点的Context
type cancelCtx struct { Context //父节点 mu sync.Mutex // 锁 done chan struct{} // channel,用于关闭 children map[canceler]struct{} // 用于储存子节点中的cancelCtx err error // set to non-nil by the first cancel call } //重写了Value方法,当key为cancelCtxKey时,返回当前的cancelCtx,否则不断向上递归寻找cancelCtx func (c *cancelCtx) Value(key interface{}) interface{} { if key == &cancelCtxKey { return c } return c.Context.Value(key) } //重写了Done方法 func (c *cancelCtx) Done() <-chan struct{} { //上锁 c.mu.Lock() //初始化done的channel,根节点的Done()方法返回的是nil if c.done == nil { c.done = make(chan struct{}) } d := c.done //解锁 c.mu.Unlock() return d } func (c *cancelCtx) Err() error { c.mu.Lock() err := c.err c.mu.Unlock() return err } var closedchan = make(chan struct{}) //用于控制取消操作的接口,其中因为cancelCtx实现了cancel方法和Done()方法,所以默认实现该接口 type canceler interface { cancel(removeFromParent bool, err error) Done() <-chan struct{} } func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { if parent == nil { //检查父节点 panic("cannot create context from nil parent") } c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) } } //创建cancelCtx结构体,并绑定父节点 func newCancelCtx(parent Context) cancelCtx { return cancelCtx{Context: parent} }
WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu. deadline time.Time } func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { if parent == nil { panic("cannot create context from nil parent") } if cur, ok := parent.Deadline(); ok && cur.Before(d) { // The current deadline is already sooner than the new one. return WithCancel(parent) } //把当前的父节点用一个中间对象cancelCtx做转换,同时绑定到timeCtx中 //约等于 temp := cancelCtx{Context: parent} // c := &timerCtx{ // cancelCtx: temp, // deadline: d, // } c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: d, } propagateCancel(parent, c) dur := time.Until(d) if dur <= 0 { c.cancel(true, DeadlineExceeded) // deadline has already passed return c, func() { c.cancel(false, Canceled) } } c.mu.Lock() defer c.mu.Unlock() if c.err == nil { c.timer = time.AfterFunc(dur, func() { c.cancel(true, DeadlineExceeded) }) } return c, func() { c.cancel(true, Canceled) } }
WithTimeout(parent Context, timeout time.Duration)
//相当于在当前时间dealline的基础上,往后延迟一段时间,所以可以调用WithDeadline方法 func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) }
context包中的方法是线程安全吗?
是线程安全
因为每次执行WithXXX方法,都会新建一个context对象,并且把父对象进行绑定。
见demo
func main() { ctx := context.WithValue(context.Background(), "xiaofu", "test") go func() { _ = context.WithValue(ctx, "xiaofu", "test1") }() go func() { _ = context.WithValue(ctx, "xiaofu", "test2") }() fmt.Println(ctx.Value("xiaofu")) fmt.Println(ctx.Value("xiaofu")) time.Sleep(3 * time.Second) } //输出 //test //test
这篇关于Context包源码解析(附面经)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-04el-table 开启定时器下,表格的选中状态会消失是什么原因-icode9专业技术文章分享
- 2024-10-03如何安装和初始化飞牛私有云 fnOS?-icode9专业技术文章分享
- 2024-10-03如何安装 App 并连接到飞牛 NAS?-icode9专业技术文章分享
- 2024-10-03如何安装飞牛 TV 并连接到影视服务器?-icode9专业技术文章分享
- 2024-10-03如何在PVE和ESXI上安装飞牛私有云 fnOS?-icode9专业技术文章分享
- 2024-10-03fnOS国产最强NAS安装系统异常情况处理-icode9专业技术文章分享
- 2024-10-03飞牛NAS如何创建存储空间?-icode9专业技术文章分享
- 2024-10-03fnOS国产最强NAS硬盘会自动休眠吗?-icode9专业技术文章分享
- 2024-10-03fnOS国产最强NAS如何安装飞牛影视和创建媒体库?-icode9专业技术文章分享
- 2024-10-03fnOS国产最强NAS如何为家人朋友开通影视账号?-icode9专业技术文章分享