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
}
相关推荐
柒和远方1 小时前
Worker Readiness:为什么 `/health` 不能证明后台任务能跑
前端·后端·架构
嘟嘟07171 小时前
Node.js spawn 实现迷你 Cursor 自动化终端|子进程完整解析
前端·后端·面试
SimonKing1 小时前
一文终结 SpringBoot 国际化!从 0 到 1 完整案例(中/英/日)
java·后端·程序员
轻松的小鸭子1 小时前
细说ASP.NET Forms身份认证
后端·asp.net
史呆芬1 小时前
CodeX多仓库提交Skill搭建
git·后端·openai
埃博拉酱1 小时前
ZEISS Microscopy Installer、ZLMT 及 ZEN 系列软件均因 FlexNet Publisher DLL 崩溃而闪退
后端
fliter1 小时前
Uber 如何完成超大规模 JUnit 迁移:从 JUnit 4 到 JUnit 5 的自动化工程实践
后端
CodeSheep2 小时前
南京IT公司六小龙
前端·后端·程序员