• golang context原理


    Context

    一个接口,四种实现,六种创建方法

    一个接口

    type Context interface {
        Deadline() (deadline time.Time, ok bool)
        Done() <-chan struct{}
        Err() error
        Value(key interface{}) interface{}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Go 的 Context接口的数据结构包含 Deadline,Done,Err,Value四个基本方法Deadline 方法返回一个 time.Time,表示当前 Context 应该结束的时间,ok 则表示有结束时间,Done 方法当 Context 被取消或者超时时候返回的一个 close 的 channel,告诉给 context 相关的函数要停止当前工作然后返回了,Err 表示 context 被取消的原因,Value 方法表示 context 实现共享数据存储的地方,是协程安全的。context 在业务中是经常被使用的。

    四种实现

    • emptyCtx 实现了一个空的context,可以用作根节点
    • cancelCtx 实现一个带cancel功能的context,可以主动取消
    • timerCtx 实现一个通过定时器timer和截止时间deadline定时取消的context
    • valueCtx 实现一个可以通过 key、val 两个字段来存数据的context

    六种创建方法

    • Background 返回一个emptyCtx作为根节点
    • TODO 返回一个emptyCtx作为未知节点
    • WithCancel 返回一个cancelCtx
    • WithDeadline 返回一个timerCtx
    • WithTimeout 返回一个timerCtx
    • WithValue 返回一个valueCtx

    使用场景

    • 上下文控制
    • 多个goroutine之间数据交互
    • 超时控制

    emptyCtx

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    emptyCtx实现了空的Context接口,其主要作用是为Background和TODO这两个方法都会返回预先初始化好的私有变量 background 和 todo,它们会在同一个 Go 程序中被复用

    Background和Todo得到的context有什么区别?

    Background通常被用于主函数、初始化以及测试中,作为一个顶层的context,也就是说一般我们创建的context都是基于Background;而TODO是在不确定使用什么context的时候才会使用。

    cancelCtx

    父子绑定重点方法

    func propagateCancel(parent Context, child canceler) {
        // 1.如果 parent ctx 是不可取消的 ctx,则直接返回 不进行关联
    	done := parent.Done()
    	if done == nil {
    		return // parent is never canceled
    	}
        // 2.接着判断一下 父ctx 是否已经被取消
    	select {
    	case <-done:
            // 2.1 如果 父ctx 已经被取消了,那就没必要关联了
            // 然后这里也要顺便把子ctx给取消了,因为父ctx取消了 子ctx就应该被取消
            // 这里是因为还没有关联上,所以需要手动触发取消
    		// parent is already canceled
    		child.cancel(false, parent.Err())
    		return
    	default:
    	}
        // 3. 从父 ctx 中提取出 cancelCtx 并将子ctx加入到父ctx 的 children 里面
    	if p, ok := parentCancelCtx(parent); ok {
    		p.mu.Lock()
            // double check 一下,确认父 ctx 是否被取消
    		if p.err != nil {
                // 取消了就直接把当前这个子ctx给取消了
    			// parent has already been canceled
    			child.cancel(false, p.err)
    		} else {
                // 否则就添加到 children 里面
    			if p.children == nil {
    				p.children = make(map[canceler]struct{})
    			}
    			p.children[child] = struct{}{}
    		}
    		p.mu.Unlock()
    	} else {
            // 如果没有找到可取消的父 context。新启动一个协程监控父节点或子节点取消信号
    		atomic.AddInt32(&goroutines, +1)
    		go func() {
    			select {
    			case <-parent.Done():
    				child.cancel(false, parent.Err())
    			case <-child.Done():
    			}
    		}()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    valueCtx

    父子查询重点方法
    func (c *valueCtx) Value(key interface{}) interface{} {
    	if c.key == key {
    		return c.val
    	}
    	return c.Context.Value(key)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    递归相当于链表从尾开始查询

    timerCtx

    type timerCtx struct {
    	cancelCtx
    	timer *time.Timer // Under cancelCtx.mu.
    
    	deadline time.Time
    }
    func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
    	return c.deadline, true
    }
    func (c *timerCtx) cancel(removeFromParent bool, err error) {
    	c.cancelCtx.cancel(false, err)
    	if removeFromParent {
    		removeChild(c.cancelCtx.Context, c)
    	}
    	c.mu.Lock()
    	if c.timer != nil {
    		c.timer.Stop()
    		c.timer = nil
    	}
    	c.mu.Unlock()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    20--Django-项目实战-博客开发之终章-文章评论、添加文章功能实现
    使用 Prometheus 监控 eKuiper 规则运行状态
    基于Python3的Scapy构造DNS报文
    关于Copy On Write Array List,你会安全使用么
    点餐系统数据库设计--SQL Server
    Driver8833电机驱动模块的使用(STM32为主控)
    【Docker系列】跨平台 Docker 镜像构建:深入理解`--platform`参数
    燃气管网监测系统,让城市生命线更安全
    数据分析 -- numpy
    【三维世界】高性能图形渲染技术——Shader你又了解多少?
  • 原文地址:https://blog.csdn.net/weixin_41154636/article/details/127445706