了解content
在 Go (Golang) 中,context 是一个标准库包,用于在 goroutine 之间传递请求范围的值、取消信号和截止时间。它是编写高并发程序的一个重要工具,尤其是在处理 API 请求、微服务通信或长时间运行的任务时。
context 包的核心是 context.Context 接口,它定义了四个方法
type Context interface {
Deadline() (deadline time.Time, ok bool) // 返回 context 的截止时间
Done() <-chan struct{} // 返回一个 channel,当 context 被取消或超时时,该 channel 会关闭
Err() error // 返回 Done 关闭的原因(取消或超时)
Value(key interface{}) interface{} // 用于存储和获取请求范围的值
}
如何使用?
创建
context.Background(): 用于主函数、初始化或者测试中。context.TODO(): 用于尚未确定应该使用哪种 Context 的地方。
派生 Context
-
context.WithCancel(parent Context): 返回一个可取消的子 Context,通过调用返回的取消函数cancel(),可以通知子 Context 及其派生的所有 Context 终止。 -
context.WithDeadline(parent Context, d time.Time): 返回一个具有截止时间的子 Context,当截止时间到达时,Done会关闭。 -
context.WithTimeout(parent Context, timeout time.Duration): 和WithDeadline类似,但直接指定超时时间。 -
context.WithValue(parent Context, key, val interface{}): 返回一个携带键值对的子 Context,用于传递请求范围的值。
示例代码
- 使用
WithCancel管理 goroutine 的生命周期
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Goroutine stopped:", ctx.Err())
return
default:
fmt.Println("Working...")
time.Sleep(500 * time.Millisecond)
}
}
}(ctx)
time.Sleep(2 * time.Second)
cancel() // 取消 context
time.Sleep(1 * time.Second)
}
- 使用
WithTimeout实现超时控制
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-time.After(3 * time.Second):
fmt.Println("Operation completed")
case <-ctx.Done():
fmt.Println("Timeout:", ctx.Err())
}
}
- 使用
WithValue传递请求范围的值
package main
import (
"context"
"fmt"
)
func main() {
type key string
myKey := key("username")
ctx := context.WithValue(context.Background(), myKey, "gopher")
process(ctx, myKey)
}
func process(ctx context.Context, k interface{}) {
if v := ctx.Value(k); v != nil {
fmt.Println("Value found:", v)
} else {
fmt.Println("Value not found")
}
}
注意事项
-
不要滥用
WithValue:WithValue适合传递请求范围的元信息(如用户 ID、认证 token 等),但不适合用作全局变量或共享数据传递。 -
及时取消 Context:使用
WithCancel、WithTimeout和WithDeadline时,务必调用返回的取消函数以避免资源泄漏。 -
避免深层嵌套:Context 的嵌套过深会导致代码复杂性增加,难以维护