go-context
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Context Usage
Go Context 使用方法
context.ContextBased on Go Wiki CodeReviewComments - Contexts.
context.ContextContext 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 as a parameter
to each method that needs it:
ctxgo
// 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成员。相反,要将作为参数传递给每个需要它的方法:
ctxgo
// 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
in function signatures:
context.Contextgo
// 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.Contextgo
// 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:
- Function parameters - Most explicit and type-safe
- Receiver - For data that belongs to the type
- Globals - For truly global configuration (use sparingly)
- 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
如果需要传递应用数据,请按以下优先级选择方式:
- 函数参数 - 最显式且类型安全
- 接收者 - 适用于属于该类型的数据
- 全局变量 - 仅用于真正的全局配置(谨慎使用)
- 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 to multiple calls that
share the same deadline, cancellation signal, credentials, and parent trace:
ctxgo
// 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是不可变的。将同一个传递给多个共享相同截止时间、取消信号、凭证和父追踪的调用是安全的:
ctxgo
// 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 only for functions that are never request-specific:
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)
}Default to passing a Context even if you think you don't need to. Only use
directly if you have a good reason why passing a context
would be a mistake:
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
}仅在非请求相关的函数中使用:
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
快速参考
| Pattern | Guidance |
|---|---|
| Parameter position | Always first: |
| Struct storage | Don't store in structs; pass to methods |
| Custom types | Don't create; use |
| Application data | Prefer parameters > receiver > globals > context values |
| Request-scoped data | Appropriate for context values |
| Sharing context | Safe - contexts are immutable |
| Only for non-request-specific code |
| Default | Pass context even if you think you don't need it |
| 模式 | 指导原则 |
|---|---|
| 参数位置 | 始终作为首个参数: |
| 结构体存储 | 不要存储在结构体中;传递给方法 |
| 自定义类型 | 不要创建;使用 |
| 应用数据 | 优先选择参数 > 接收者 > 全局变量 > Context值 |
| 请求作用域数据 | 适合存入Context值 |
| 共享Context | 安全 - Context是不可变的 |
| 仅用于非请求相关代码 |
| 默认做法 | 即使认为不需要也传递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的接口设计模式