var ( background = new(emptyCtx) todo = new(emptyCtx) )
funcBackground() Context { return background }
funcTODO() Context { return todo }
3 cancelCtx
3.1 cancelCtx 数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
type cancelCtx struct { Context //embed 了一个 context 作为其父 context. 可见,cancelCtx 必然为某个 context 的子 context; // 内置了一把锁,用以协调并发场景下的资源获取; mu sync.Mutex // protects following fields done atomic.Value // of chan struct{}, created lazily, closed by first cancel call 。实际类型为 chan struct{},即用以反映 cancelCtx 生命周期的通道;怎么懒加载的可以看下面贴的 Done 方法源码 children map[canceler]struct{} // set to nil by the first cancel call 一个 set,指向 cancelCtx 的所有子 context;那什么是 canceler 呢,下面贴了 err error// set to non-nil by the first cancel call 记录了当前 cancelCtx 的错误. 必然为某个 context 的子 context; }
两个入参,第一个 removeFromParent 是一个 bool 值,表示当前 context 是否需要从父 context 的 children set 中删除;第二个 err 则是 cancel 后需要展示的错误; func(c *cancelCtx) cancel(removeFromParent bool, err error) { if err == nil { panic("context: internal error: missing cancel error") } c.mu.Lock() if c.err != nil { //若非空说明已被 cancel,则解锁返回; c.mu.Unlock() return// already canceled } c.err = err d, _ := c.done.Load().(chanstruct{}) if d == nil { // 懒加载。还没声明过。则取一个全局的closedchan注入 c.done.Store(closedchan) } else { close(d) } for child := range c.children { // NOTE: acquiring the child's lock while holding parent's lock. child.cancel(false, err)//所有孩子都要👋🏻 } c.children = nil c.mu.Unlock()
if removeFromParent { // 根据传入的 removeFromParent flag 判断是否需要手动把 cancelCtx 从 parent 的 children set 中移除. removeChild(c.Context, c) //c.Context就是 c 的父亲 } }
走进 removeChild 方法中,观察如何将 cancelCtx 从 parent 的 children set 中移除:
1 2 3 4 5 6 7 8 9 10 11 12 13
funcremoveChild(parent Context, child canceler) { p, ok := parentCancelCtx(parent)//小蝌蚪找爸爸 if !ok { // 爸爸不是 cancelCtx,直接返回 return } p.mu.Lock() if p.children != nil { // 爸爸弃孩子 delete(p.children, child) } p.mu.Unlock() }
关于 removeFromParent 为啥有时候传 false ,我问了 chatGPT:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
在 go 的源码里,为啥 cancelCtx 的 cancel 方法第一个参数removeFromParent,有时候传 true 有时候传 false === 在 Go 的源码中,cancelCtx 结构体的 cancel 方法中的 removeFromParent 参数用于指示在取消操作后是否从父上下文中移除该子上下文。