go-context

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go Context Usage

Go Context 使用方法

context.Context
carries security credentials, tracing information, deadlines, and cancellation signals across API and process boundaries. Go programs pass contexts explicitly along the entire function call chain.

context.Context
可在API和进程边界之间传递安全凭证、追踪信息、截止时间以及取消信号。Go程序会在整个函数调用链中显式传递上下文。

Context as First Parameter

作为首个参数

Functions that use a Context should accept it as their first parameter:
go
// Good: Context is first parameter
func F(ctx context.Context, /* other arguments */) error {
    // ...
}

func ProcessRequest(ctx context.Context, req *Request) (*Response, error) {
    // ...
}
This is a strong convention in Go that makes context flow visible and consistent across codebases.

使用Context的函数应将其作为首个参数
go
// Good: Context is first parameter
func F(ctx context.Context, /* other arguments */) error {
    // ...
}

func ProcessRequest(ctx context.Context, req *Request) (*Response, error) {
    // ...
}
这是Go中的一个强约定,能让上下文的流转在代码库中清晰且一致。

Don't Store Context in Structs

不要在结构体中存储Context

Do not add a Context member to a struct type. Instead, pass
ctx
as a parameter to each method that needs it:
go
// Bad: Context stored in struct
type Worker struct {
    ctx context.Context  // Don't do this
    // ...
}

func (w *Worker) Process() error {
    // Uses w.ctx - context lifetime unclear
}
go
// Good: Context passed to methods
type Worker struct {
    // ...
}

func (w *Worker) Process(ctx context.Context) error {
    // Context explicitly passed - lifetime clear
}
Exception: Methods whose signature must match an interface in the standard library or a third-party library may need to work around this.

不要为结构体类型添加Context成员。相反,要将
ctx
作为参数传递给每个需要它的方法:
go
// Bad: Context stored in struct
type Worker struct {
    ctx context.Context  // Don't do this
    // ...
}

func (w *Worker) Process() error {
    // Uses w.ctx - context lifetime unclear
}
go
// Good: Context passed to methods
type Worker struct {
    // ...
}

func (w *Worker) Process(ctx context.Context) error {
    // Context explicitly passed - lifetime clear
}
例外情况:方法签名必须匹配标准库或第三方库中的接口时,可能需要变通处理。

Don't Create Custom Context Types

不要创建自定义Context类型

Do not create custom Context types or use interfaces other than
context.Context
in function signatures:
go
// Bad: Custom context type
type MyContext interface {
    context.Context
    GetUserID() string
}

func Process(ctx MyContext) error { ... }
go
// Good: Use standard context.Context
func Process(ctx context.Context) error {
    userID := GetUserID(ctx)  // Extract from context value
    // ...
}

不要创建自定义Context类型,也不要在函数签名中使用
context.Context
以外的接口:
go
// Bad: Custom context type
type MyContext interface {
    context.Context
    GetUserID() string
}

func Process(ctx MyContext) error { ... }
go
// Good: Use standard context.Context
func Process(ctx context.Context) error {
    userID := GetUserID(ctx)  // Extract from context value
    // ...
}

Where to Put Application Data

应用数据的存放位置

If you have application data to pass around, consider these options in order of preference:
  1. Function parameters - Most explicit and type-safe
  2. Receiver - For data that belongs to the type
  3. Globals - For truly global configuration (use sparingly)
  4. Context value - Only if it truly belongs there (request-scoped data)
go
// Good: Explicit parameter
func ProcessOrder(ctx context.Context, userID string, order *Order) error {
    // userID is explicit
}

// Good: Context value for request-scoped data
func ProcessOrder(ctx context.Context, order *Order) error {
    // Request ID from context is appropriate - it's request-scoped
    reqID := RequestIDFromContext(ctx)
    // ...
}
Context values are appropriate for:
  • Request IDs and trace IDs
  • Authentication/authorization info that flows with requests
  • Deadlines and cancellation signals
Context values are not appropriate for:
  • Optional function parameters
  • Data that could be passed explicitly
  • Configuration that doesn't vary per-request

如果需要传递应用数据,请按以下优先级选择方式:
  1. 函数参数 - 最显式且类型安全
  2. 接收者 - 适用于属于该类型的数据
  3. 全局变量 - 仅用于真正的全局配置(谨慎使用)
  4. Context值 - 仅当数据确实属于此处时使用(请求作用域数据)
go
// Good: Explicit parameter
func ProcessOrder(ctx context.Context, userID string, order *Order) error {
    // userID is explicit
}

// Good: Context value for request-scoped data
func ProcessOrder(ctx context.Context, order *Order) error {
    // Request ID from context is appropriate - it's request-scoped
    reqID := RequestIDFromContext(ctx)
    // ...
}
Context值适用于:
  • 请求ID和追踪ID
  • 随请求流转的认证/授权信息
  • 截止时间和取消信号
Context值不适用于
  • 可选函数参数
  • 可显式传递的数据
  • 不随请求变化的配置

Context Immutability

Context的不可变性

Contexts are immutable. It's safe to pass the same
ctx
to multiple calls that share the same deadline, cancellation signal, credentials, and parent trace:
go
// Good: Same context to multiple calls
func ProcessBatch(ctx context.Context, items []Item) error {
    for _, item := range items {
        // Safe to pass same ctx to each call
        if err := process(ctx, item); err != nil {
            return err
        }
    }
    return nil
}

// Good: Same context to concurrent calls
func ProcessConcurrently(ctx context.Context, a, b *Data) error {
    g, ctx := errgroup.WithContext(ctx)
    g.Go(func() error { return processA(ctx, a) })
    g.Go(func() error { return processB(ctx, b) })
    return g.Wait()
}

Context是不可变的。将同一个
ctx
传递给多个共享相同截止时间、取消信号、凭证和父追踪的调用是安全的:
go
// Good: Same context to multiple calls
func ProcessBatch(ctx context.Context, items []Item) error {
    for _, item := range items {
        // Safe to pass same ctx to each call
        if err := process(ctx, item); err != nil {
            return err
        }
    }
    return nil
}

// Good: Same context to concurrent calls
func ProcessConcurrently(ctx context.Context, a, b *Data) error {
    g, ctx := errgroup.WithContext(ctx)
    g.Go(func() error { return processA(ctx, a) })
    g.Go(func() error { return processB(ctx, b) })
    return g.Wait()
}

When to Use context.Background()

何时使用context.Background()

Use
context.Background()
only for functions that are never request-specific:
go
// Good: Main function or initialization
func main() {
    ctx := context.Background()
    if err := run(ctx); err != nil {
        log.Fatal(err)
    }
}

// Good: Top-level background task
func startBackgroundWorker() {
    ctx := context.Background()
    go worker(ctx)
}
Default to passing a Context even if you think you don't need to. Only use
context.Background()
directly if you have a good reason why passing a context would be a mistake:
go
// Prefer: Accept context even for "simple" operations
func LoadConfig(ctx context.Context) (*Config, error) {
    // Even if not using ctx now, accepting it allows future
    // additions without API changes
}

仅在非请求相关的函数中使用
context.Background()
go
// Good: Main function or initialization
func main() {
    ctx := context.Background()
    if err := run(ctx); err != nil {
        log.Fatal(err)
    }
}

// Good: Top-level background task
func startBackgroundWorker() {
    ctx := context.Background()
    go worker(ctx)
}
默认传递Context,即使你认为不需要。只有当你有充分理由认为传递上下文是错误的时,才直接使用
context.Background()
go
// Prefer: Accept context even for "simple" operations
func LoadConfig(ctx context.Context) (*Config, error) {
    // Even if not using ctx now, accepting it allows future
    // additions without API changes
}

Common Patterns

常见模式

Deriving Contexts

派生Context

go
// Add timeout
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

// Add cancellation
ctx, cancel := context.WithCancel(ctx)
defer cancel()

// Add deadline
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Hour))
defer cancel()

// Add value (use sparingly)
ctx = context.WithValue(ctx, requestIDKey, reqID)
go
// Add timeout
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

// Add cancellation
ctx, cancel := context.WithCancel(ctx)
defer cancel()

// Add deadline
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Hour))
defer cancel()

// Add value (use sparingly)
ctx = context.WithValue(ctx, requestIDKey, reqID)

Checking Cancellation

检查取消

go
func LongRunningOperation(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()
        default:
            // Do work
        }
    }
}
go
func LongRunningOperation(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()
        default:
            // Do work
        }
    }
}

Respecting Cancellation in HTTP Handlers

在HTTP处理器中遵守取消机制

go
func handler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    
    result, err := slowOperation(ctx)
    if err != nil {
        if errors.Is(err, context.Canceled) {
            // Client disconnected
            return
        }
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    json.NewEncoder(w).Encode(result)
}

go
func handler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    
    result, err := slowOperation(ctx)
    if err != nil {
        if errors.Is(err, context.Canceled) {
            // Client disconnected
            return
        }
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    json.NewEncoder(w).Encode(result)
}

Quick Reference

快速参考

PatternGuidance
Parameter positionAlways first:
func F(ctx context.Context, ...)
Struct storageDon't store in structs; pass to methods
Custom typesDon't create; use
context.Context
interface
Application dataPrefer parameters > receiver > globals > context values
Request-scoped dataAppropriate for context values
Sharing contextSafe - contexts are immutable
context.Background()
Only for non-request-specific code
DefaultPass context even if you think you don't need it

模式指导原则
参数位置始终作为首个参数:
func F(ctx context.Context, ...)
结构体存储不要存储在结构体中;传递给方法
自定义类型不要创建;使用
context.Context
接口
应用数据优先选择参数 > 接收者 > 全局变量 > Context值
请求作用域数据适合存入Context值
共享Context安全 - Context是不可变的
context.Background()
仅用于非请求相关代码
默认做法即使认为不需要也传递Context

See Also

另请参阅

  • go-concurrency: Goroutine patterns, cancellation, and coordination
  • go-error-handling: Handling context cancellation errors
  • go-interfaces: Interface design patterns for context-accepting APIs
  • go-concurrency:协程模式、取消和协调
  • go-error-handling:处理Context取消错误
  • go-interfaces:适用于接收Context的API的接口设计模式