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
}
相关推荐
颜酱9 小时前
07 | 把字段与指标同步到 Qdrant(生成阶段)
前端·人工智能·后端
Larcher9 小时前
从“加载模型”界面到端侧推理:拆解一个 React + WebGPU 大模型 Demo
javascript·后端
Larcher10 小时前
从状态快照到惰性初始化:读懂 React useState 的三个关键场景
javascript·人工智能·后端
lazy H10 小时前
Git clone 怎么用?克隆项目及常见问题完整教程
大数据·git·后端·学习·搜索引擎·github
wang090710 小时前
自己动手写一个spring之aop_1
java·后端·spring
神奇小汤圆10 小时前
IDEA 运行报 Command line is too long?别慌,两招搞定(附原理)
后端
SelectDB11 小时前
Apache Doris 4.1 全面增强 Iceberg:支持 UPDATE、MERGE INTO 与 Iceberg V3
后端
Conan在掘金11 小时前
ArkTS 进阶之道(13):ForEach 循环渲染边界——为啥 build 里不能写 for 循环
后端
妙码生花11 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(四十一):增加管理员账号管理接口
后端·go·gin
用户06782607432711 小时前
APP版本管理全链路(后端设计)
后端