Context #
一、Context概述 #
Context用于在goroutine之间传递取消信号、超时、截止时间和请求范围的值。
二、创建Context #
2.1 Background #
go
ctx := context.Background()
作为根context,通常在main函数或测试中使用。
2.2 TODO #
go
ctx := context.TODO()
当不确定使用哪个context时使用。
2.3 WithCancel #
go
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
<-ctx.Done()
fmt.Println("Canceled")
}()
time.Sleep(time.Second)
cancel()
2.4 WithTimeout #
go
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
select {
case <-ctx.Done():
fmt.Println("Timeout:", ctx.Err())
}
2.5 WithDeadline #
go
deadline := time.Now().Add(time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
select {
case <-ctx.Done():
fmt.Println("Deadline reached")
}
2.6 WithValue #
go
ctx := context.WithValue(context.Background(), "key", "value")
v := ctx.Value("key")
fmt.Println(v) // value
三、Context接口 #
3.1 接口定义 #
go
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
3.2 方法说明 #
| 方法 | 说明 |
|---|---|
| Deadline | 返回截止时间 |
| Done | 返回取消通道 |
| Err | 返回取消原因 |
| Value | 获取关联值 |
四、取消机制 #
4.1 主动取消 #
go
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Worker stopped")
return
default:
// 工作
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(time.Second)
cancel()
time.Sleep(time.Millisecond * 100)
}
4.2 级联取消 #
go
func main() {
ctx1, cancel1 := context.WithCancel(context.Background())
ctx2, _ := context.WithCancel(ctx1)
go func() {
<-ctx2.Done()
fmt.Println("ctx2 canceled")
}()
cancel1() // ctx2也会被取消
}
五、超时控制 #
5.1 HTTP请求超时 #
go
func fetch(url string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
5.2 数据库查询超时 #
go
func queryWithTimeout(db *sql.DB, query string) (*sql.Rows, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
return db.QueryContext(ctx, query)
}
5.3 操作超时 #
go
func operation(ctx context.Context) error {
select {
case <-time.After(2 * time.Second):
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := operation(ctx); err != nil {
fmt.Println("Error:", err)
}
}
六、值传递 #
6.1 请求ID #
go
type key string
const requestIDKey key = "requestID"
func WithRequestID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, requestIDKey, id)
}
func GetRequestID(ctx context.Context) string {
if v := ctx.Value(requestIDKey); v != nil {
return v.(string)
}
return ""
}
6.2 用户信息 #
go
type User struct {
ID int
Name string
}
const userKey key = "user"
func WithUser(ctx context.Context, user *User) context.Context {
return context.WithValue(ctx, userKey, user)
}
func GetUser(ctx context.Context) *User {
if v := ctx.Value(userKey); v != nil {
return v.(*User)
}
return nil
}
6.3 注意事项 #
- 不要用于传递可选参数
- 使用自定义类型作为key避免冲突
- 值应该是不可变的
七、实际应用 #
7.1 HTTP中间件 #
go
func WithTimeoutMiddleware(timeout time.Duration) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), timeout)
defer cancel()
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
7.2 优雅关闭 #
go
func main() {
server := &http.Server{Addr: ":8080"}
go func() {
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server.Shutdown(ctx)
}
7.3 并发控制 #
go
func processAll(ctx context.Context, items []int) error {
var wg sync.WaitGroup
errCh := make(chan error, len(items))
for _, item := range items {
wg.Add(1)
go func(item int) {
defer wg.Done()
if err := process(ctx, item); err != nil {
errCh <- err
}
}(item)
}
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case err := <-errCh:
return err
case <-done:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
八、最佳实践 #
8.1 函数第一个参数 #
go
func DoSomething(ctx context.Context, arg Arg) error {
// ...
}
8.2 不要存储Context #
go
// 错误
type MyStruct struct {
ctx context.Context
}
// 正确:显式传递
func (s *MyStruct) Do(ctx context.Context) {}
8.3 调用cancel #
go
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() // 释放资源
8.4 不要传递nil #
go
// 错误
func Do(ctx context.Context) {}
Do(nil)
// 正确
Do(context.Background())
九、常见错误 #
9.1 忘记调用cancel #
go
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
// 忘记defer cancel()
9.2 阻塞Done通道 #
go
select {
case <-ctx.Done():
// 不要在这里阻塞
}
9.3 滥用WithValue #
go
// 不要用于传递可选参数
ctx = context.WithValue(ctx, "timeout", time.Second)
十、总结 #
Context要点:
| 函数 | 用途 |
|---|---|
| Background | 根context |
| TODO | 占位context |
| WithCancel | 可取消context |
| WithTimeout | 超时context |
| WithDeadline | 截止时间context |
| WithValue | 携带值的context |
关键点:
- 取消传播:取消信号会级联传播
- 超时控制:用于控制操作超时
- 值传递:传递请求范围的数据
- 调用cancel:必须调用cancel释放资源
- 第一个参数:context作为函数第一个参数
准备好学习Channel了吗?让我们进入下一章!
最后更新:2026-03-26