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
}
相关推荐
元界metalite25 分钟前
SpringBoot整合Druid-连接池参数为什么不能照抄
后端
fatcoder31 分钟前
玩转Nginx 09 — 运维排障:三场"火灾"实战演练
前端·后端·nginx
元界metalite34 分钟前
MyBatis通用查询如何避免SELECT *?MetaLite如何按需返回字段
后端
Flynt1 小时前
Go 1.27 升了一波,泛型方法和 JSON v2 真香但有个坑
后端·go
我会冲击波1 小时前
vibe coding 一个多月,我把 Claude Code + Pi 做成了桌面 IDE,还给它移植了 VS Code 的编辑体验
前端·javascript·后端
我是谁的程序员2 小时前
Windows / Linux / Mac 上不用 Xcode 把 IPA 上传到 App Store,upload 命令详解
后端·ios
凌涘2 小时前
JWT 登录鉴权 Demo 拆解
后端
JimmtButler2 小时前
从 Java 到 JS:我终于把闭包想明白了
javascript·后端
掘金者阿豪2 小时前
Codex 怎么突然变慢了?一个需求跑几十分钟,我才发现它的工作方式已经变了
前端·后端
Zadig2 小时前
Zadig 全面支持 CRD,至此所有 K8s 资源类型均可一键发布!
后端·devops