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
}
相关推荐
蜗牛互联网4 小时前
WSL Containers GA:本地AI容器的生命周期、网络与治理验收
java·网络·人工智能·后端
Zhou1411366 小时前
SpringBoot_02_自动配置原理
java·spring boot·后端
子兮曰7 小时前
Node.js 50个优势场景盘点:一个人单干,为啥我多数时候只用它
前端·后端
IT_陈寒7 小时前
我花3小时debug,就因为JavaScript这个隐式转换坑
前端·人工智能·后端
程序猿阿越7 小时前
containerd如何拉取镜像
后端·kubernetes·源码阅读
一条小小yu7 小时前
为什么使用springboot
java·spring boot·后端
香瓜子rd7 小时前
MySQL InnoDB 并发控制核心原理:事务、隔离级别、MVCC 与锁机制
数据库·后端
RISCV_Explorer7 小时前
RISC-V RVV向量编程模型机制解析——vsetvl配置、掩码与尾元素策略及长度无关编程
后端·risc-v
对象存储与RustFS7 小时前
升级 RustFS 二进制不停机:一条一条换,留一条退路
后端·rust·开源
hasty8 小时前
限制写了却没生效:OpenTelemetry Go 的 Unicode 截断边界
开发语言·后端·golang