Golang 中的 Context
在 Golang 中,Context 是一个非常重要的概念,它用于在 goroutine 之间传递请求范围的值、取消信号和截止时间。Context 包是在 Go 1.7 版本中引入的,它提供了一种优雅的方式来处理并发程序中的取消操作和超时控制。
Context 的基本概念
Context 是一个接口,定义了四个方法:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Deadline(): 返回 context 的截止时间
Done(): 返回一个 channel,当 context 被取消时会关闭
Err(): 返回 context 被取消的原因
Value(): 返回与 key 关联的值
Context 的类型
1. Background Context
context.Background() 是所有 Context 的根,它永远不会被取消,没有值,也没有截止时间。通常用作主函数、初始化和测试的起点。
ctx := context.Background()
2. TODO Context
context.TODO() 当不确定使用哪个 Context 或者还没有可用的 Context 时使用。
3. WithCancel
context.WithCancel() 返回一个可取消的 Context 和一个取消函数。
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 确保释放资源
4. WithTimeout
context.WithTimeout() 返回一个带有超时的 Context。
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
5. WithDeadline
context.WithDeadline() 返回一个带有截止时间的 Context。
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
6. WithValue
context.WithValue() 返回一个携带键值对的 Context。
ctx := context.WithValue(context.Background(), "userID", 12345)
使用示例
超时控制
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-time.After(3 * time.Second):
fmt.Println("操作完成")
case <-ctx.Done():
fmt.Println("操作超时:", ctx.Err())
}
}
HTTP 请求中的使用
func fetchData(ctx context.Context, url string) error {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 处理响应
return nil
}
传递值
type key string
const userIDKey key = "userID"
func main() {
ctx := context.WithValue(context.Background(), userIDKey, "user123")
processRequest(ctx)
}
func processRequest(ctx context.Context) {
userID := ctx.Value(userIDKey)
if userID != nil {
fmt.Printf("处理用户 %s 的请求\n", userID)
}
}
最佳实践
-
总是将 Context 作为第一个参数传递
func DoSomething(ctx context.Context, arg1 string, arg2 int) error {
// ...
}
-
不要将 Context 存储在结构体中
Context 应该通过函数参数传递,而不是存储在结构体中。
-
不要传递 nil Context
如果不确定使用哪个 Context,使用 context.TODO()。
-
Context 的 Value 方法应该仅用于传递请求范围的数据
不要用它来传递可选参数。
-
及时调用 cancel 函数
使用 defer cancel() 确保资源被释放。
注意事项
- Context 是并发安全的,可以在多个 goroutine 中安全使用
- Context 的取消是级联的,父 Context 取消时,所有子 Context 也会被取消
- 不要忽略 Context 的 Done channel,它是取消信号的重要来源
- Context 的 Value 方法查找是递归的,会向上查找父 Context
Context 是 Go 并发编程中的重要工具,正确使用它可以让你的程序更加健壮和高效。
Golang 中的 Context
在 Golang 中,Context 是一个非常重要的概念,它用于在 goroutine 之间传递请求范围的值、取消信号和截止时间。Context 包是在 Go 1.7 版本中引入的,它提供了一种优雅的方式来处理并发程序中的取消操作和超时控制。
Context 的基本概念
Context 是一个接口,定义了四个方法:
Deadline(): 返回 context 的截止时间Done(): 返回一个 channel,当 context 被取消时会关闭Err(): 返回 context 被取消的原因Value(): 返回与 key 关联的值Context 的类型
1. Background Context
context.Background()是所有 Context 的根,它永远不会被取消,没有值,也没有截止时间。通常用作主函数、初始化和测试的起点。2. TODO Context
context.TODO()当不确定使用哪个 Context 或者还没有可用的 Context 时使用。3. WithCancel
context.WithCancel()返回一个可取消的 Context 和一个取消函数。4. WithTimeout
context.WithTimeout()返回一个带有超时的 Context。5. WithDeadline
context.WithDeadline()返回一个带有截止时间的 Context。6. WithValue
context.WithValue()返回一个携带键值对的 Context。使用示例
超时控制
HTTP 请求中的使用
传递值
最佳实践
总是将 Context 作为第一个参数传递
不要将 Context 存储在结构体中
Context 应该通过函数参数传递,而不是存储在结构体中。
不要传递 nil Context
如果不确定使用哪个 Context,使用
context.TODO()。Context 的 Value 方法应该仅用于传递请求范围的数据
不要用它来传递可选参数。
及时调用 cancel 函数
使用
defer cancel()确保资源被释放。注意事项
Context 是 Go 并发编程中的重要工具,正确使用它可以让你的程序更加健壮和高效。