golang基础--context

103 阅读2分钟

了解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,用于传递请求范围的值。

示例代码

  1. 使用 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)
}

  1. 使用 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())
	}
}
  1. 使用 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")
	}
}

注意事项

  • 不要滥用 WithValueWithValue 适合传递请求范围的元信息(如用户 ID、认证 token 等),但不适合用作全局变量或共享数据传递。

  • 及时取消 Context:使用 WithCancelWithTimeoutWithDeadline 时,务必调用返回的取消函数以避免资源泄漏。

  • 避免深层嵌套:Context 的嵌套过深会导致代码复杂性增加,难以维护