Context 包的设计哲学
1. 设计目标
Context 主要用于在 goroutine 之间传递请求范围的值、取消信号和截止时间。它的核心设计理念包括:
- 显式传递请求上下文
- 统一的取消机制
- 可扩展的元数据传递
- 支持超时控制
2. 核心接口
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
3. 实现类型
3.1 Background & TODO
- context.Background(): 根上下文,通常用于初始化
- context.TODO(): 占位上下文,用于未确定场景
3.2 WithCancel
创建可取消的上下文:
ctx, cancel := context.WithCancel(parent)
defer cancel() // 确保资源释放
取消机制:
sequenceDiagram
participant P as Parent Context
participant C as Child Context
participant G as Goroutine
P->>C: 创建子 Context
C->>G: 启动 Goroutine
P->>P: 调用 cancel()
P->>C: 传播取消信号
C->>G: 发送取消信号
G->>G: 处理取消
G->>C: 返回
3.3 WithTimeout
创建带超时的上下文:
ctx, cancel := context.WithTimeout(parent, 5*time.Second)
defer cancel()
3.4 WithValue
传递请求范围的值:
ctx := context.WithValue(parent, "userID", 123)
值传递流程:
flowchart LR
A[Parent Context] -->|WithValue| B[Child Context]
B -->|Value| C[Goroutine 1]
B -->|Value| D[Goroutine 2]
C -->|Read| B
D -->|Read| B
4. 使用模式
4.1 请求链传递
func handler(ctx context.Context) {
// 传递上下文
go process(ctx)
}
4.2 超时控制
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-ctx.Done():
fmt.Println("Timeout")
case result := <-longRunningTask():
fmt.Println(result)
}
5. 代码示例
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
go func(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Task canceled:", ctx.Err())
case <-time.After(5 * time.Second):
fmt.Println("Task completed")
}
}(ctx)
time.Sleep(4 * time.Second)
}
6. 最佳实践
- 在函数参数中显式传递 context
- 及时调用 cancel 函数释放资源
- 避免滥用 context.Value
- 合理设置超时时间