Z
ZHANK
编程在线教程Go 教程context 上下文深入
Web 与工程化

context 上下文深入

context.Background/TODO、WithCancel/WithTimeout/WithValue、链路传递

context 上下文深入

context.Context 是 Go 并发和网络编程的基石——传递取消信号、超时控制、请求级数据。

学完本章你将: 掌握 context.Background/TODO、WithCancel/WithTimeout/WithValue。


四种创建方式

go
package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    // Background —— 根 context(main/初始化用)
    ctx := context.Background()

    // WithCancel —— 手动取消
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()  // 确保释放资源

    // WithTimeout —— 超时自动取消
    ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
    defer cancel()

    // WithDeadline —— 指定截止时间
    ctx, cancel := context.WithDeadline(ctx, time.Now().Add(5*time.Second))
    defer cancel()
}

取消传播

go
func worker(ctx context.Context, name string) {
    for {
        select {
        case <-ctx.Done():  // 收到取消信号
            fmt.Println(name, "退出")
            return
        default:
            fmt.Println(name, "工作中...")
            time.Sleep(500 * time.Millisecond)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go worker(ctx, "A")
    go worker(ctx, "B")

    time.Sleep(2 * time.Second)
    cancel()  // 同时取消 A 和 B
    time.Sleep(time.Second)
}

WithValue —— 传递请求级数据

go
type contextKey string  // 自定义 key 类型——防止冲突

ctx := context.WithValue(context.Background(), contextKey("userID"), 42)

// 取出
if userID, ok := ctx.Value(contextKey("userID")).(int); ok {
    fmt.Println("User ID:", userID)
}

// ⚠️ WithValue 只适合传递请求范围的数据(如 trace ID)
// 不要用来传函数参数——破坏类型安全