Go之context

概要

context,即上下文,用于在不同的goroutine之间同步请求特定的数据、取消信号以及处理请求的截止日期;对同一个context的操作是并发安全的

用途

1.传递数据

demo:

go 复制代码
ctx := context.WithValue(context.Background(), KEY,NewRequestID())

v, ok := ctx.Value(k).(string) 
if !ok{ return "" } 
return v

2.统一取消

demo:

css 复制代码
func main()  {
    ctx,cancel := context.WithCancel(context.Background())
    go Speak(ctx)
    time.Sleep(10*time.Second)
    cancel()
    time.Sleep(1*time.Second)
}

func Speak(ctx context.Context)  {
    for range time.Tick(time.Second){
        select {
        case <- ctx.Done():
            fmt.Println("我要闭嘴了")
            return
        default:
            fmt.Println("balabalabalabala")
        }
    }
}

3.超时处理 demo:

scss 复制代码
func main()  {
    HttpHandler()
}

func NewContextWithTimeout() (context.Context,context.CancelFunc) {
    return context.WithTimeout(context.Background(), 3 * time.Second)
}

func HttpHandler()  {
    ctx, cancel := NewContextWithTimeout()
    defer cancel()
    deal(ctx)
}

func deal(ctx context.Context)  {
    for i:=0; i< 10; i++ {
        time.Sleep(1*time.Second)
        select {
        case <- ctx.Done():
            fmt.Println(ctx.Err())
            return
        default:
            fmt.Printf("deal time is %d\n", i)
        }
    }
}

注意: 统一取消和超时处理返回的cancel方法的执行是和管道触发通知的执行逻辑是一样的

底层原理

接口:

scss 复制代码
type Context interface {
    Deadline() (deadline time.Time, ok bool)  // 是否设置了截止时间
    Done() <-chan struct{} // 取消信号 channel,关闭表示已取消/超时
    Err() error // 取消原因:`Canceled` / `DeadlineExceeded` / nil
    Value(key any) any // 按 key 取值(请求级 metadata)
}

4种接口实现:

scss 复制代码
                    Context (interface)
                         │
        ┌────────────────┼────────────────┬──────────────┐
        │                │                │              │
   emptyCtx         cancelCtx         timerCtx        valueCtx
 (Background/      (可取消)          (超时/截止)      (传值)
    TODO)

empytCtx:

go 复制代码
type emptyCtx int

var (
    background = emptyCtx(0)  // context.Background()
    todo       = emptyCtx(1)  // context.TODO()
)

cancelCtx:

go 复制代码
type cancelCtx struct {
    Context                    // 嵌入 parent
    mu       sync.Mutex
    done     atomic.Value       // 原子值,存贮chan struct{},懒创建
    children map[canceler]struct{}  // 子 context
    err      error              // 取消原因
}


func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
    c := &cancelCtx{Context: parent}
    propagateCancel(parent, c)  // 把 c 挂到 parent 的 children
    return c, func() { c.cancel(true, Canceled) }
}

func (c *cancelCtx) Done() <-chan struct{} {
    d := c.done.Load()
    if d != nil {
        return d.(chan struct{})
    }
    c.mu.Lock()
    defer c.mu.Unlock()
    d = c.done.Load()
    if d == nil {
        d = make(chan struct{})
        c.done.Store(d)
    }
    return d.(chan struct{})
}


1. 加锁
2. 若已取消 → 直接返回
3. close(done channel)     ← 所有监听 Done 的 goroutine 被唤醒
4. err = Canceled 或 DeadlineExceeded
5. 递归 cancel 所有 children
6. 从 parent.children 里移除自己
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
    c.mu.Lock()
    if c.err != nil { /* 已取消 */ return }
    c.err = err
    close(c.done.Load().(chan struct{}))
    for child := range c.children {
        child.cancel(false, err)
    }
    c.children = nil
    c.mu.Unlock()
    if removeFromParent {
        removeChild(c.Context, c)
    }
}

timerCtx:

lua 复制代码
type timerCtx struct {
    cancelCtx
    timer *time.Timer
    deadline time.Time
}

valueCtx:

arduino 复制代码
type valueCtx struct {
    Context
    key, val any
}
相关推荐
特立独行的猫A20 小时前
用仓颉语言 cj-tauri 框架,五分钟给娃做了个鸿蒙形近字游戏
java·后端·python
码事漫谈1 天前
我花了一周时间,看AI公司怎么"管"AI,结果越看越后背发凉
后端
爱学习的程序媛1 天前
Python常用框架梳理
开发语言·后端·python·ai·web·后端框架·python框架
GISer_Jing1 天前
Agent核心内容
后端·ai·langchain
budingxiaomoli1 天前
Spring统一功能处理
java·后端·spring
某鹏1 天前
网关选型:Go、Java、Graal 谁都不能完胜
后端
用户0247836530971 天前
从屎山到防线:14 行状态迁移表,给任务系统装上防瞎跳闸门
后端
CQU_JIAKE1 天前
11.13[体系结构】指标,五级流水线,八级流水线,虚实地址思考
后端
panbook1 天前
从0到1实现一个分布式时序数据库(三):主从复制、故障检测与切换
后端
上线再说1 天前
图片从 8.2MB 压到 296KB:我把小程序图片链路的账单拆了一遍
后端