Skip to content

第五章 上下文 v1.0

第一部分 context

Context类型是context包核心概念,代表了执行请求的全部生命周期,包括取消、截止时间、值传递等功能

一、创建

context包主要提供两种方式创建context,两种方式实际没有区别

go
context.Backgroud()  // 官方定义:上下文默认值,其他上下文都应从他派生出来
context.TODO()       // 官方定义:不确定应使用哪种上下文时使用,应尽早替换为具体上下文

二、派生

根context不具备任何功能,使其具备功能需要进行派生

go
// 派生可取消上下文
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
go
// 派生绝对截止时间上下文
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
go
// 派生相对超时时间上下文
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
go
// 派生携带数据的上下文
func WithValue(parent Context, key, val interface{}) Context
派生示例
go
func main() {
	ctx1, cancel1 := context.WithCancel(context.Background())
	ctx2, cancel2 := context.WithCancel(context.Background())
	defer cancel1() // 确保函数退出时调用取消函数
	defer cancel2() // 确保函数退出时调用取消函数

	go func() {
		time.Sleep(1 * time.Second)
		cancel1() // 1秒后取消操作
		time.Sleep(5 * time.Second)
		cancel2() // 5秒后取消
	}()

	select {
	case <-time.After(2 * time.Second):
		fmt.Println("ctx1 operation completed")
	case <-ctx1.Done():
		fmt.Println("ctx1 operation cancelled")  // 输出结果
	}

	select {
	case <-time.After(2 * time.Second):
		fmt.Println("ctx2 operation completed")  // 输出结果
	case <-ctx2.Done():
		fmt.Println("ctx2 operation cancelled")  
	}
}
go
func main() {
    deadline := time.Now().Add(3 * time.Second)      // 设置deadline为3秒后
    ctx, cancel := context.WithDeadline(context.Background(), deadline)
    defer cancel()

    select {
    case <-time.After(5 * time.Second):
        fmt.Println("operation completed")
    case <-ctx.Done():
        fmt.Println("operation timed out")  // 输出结果
    }
}
go
func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    select {
    case <-time.After(5 * time.Second):
        fmt.Println("operation completed")
    case <-ctx.Done():
        fmt.Println("operation timed out")  // 输出结果
    }
}
go
func main() {
    ctx := context.WithValue(context.Background(), "userID", 10000)
    processRequest(ctx)
}

func processRequest(ctx context.Context) {
    userID := ctx.Value("userID").(int)
    fmt.Printf("Processing request for user ID: %d\n", userID)  // 输出:Processing request for user ID: 10000
}

基于一个父Context可以随意衍生,形成一棵Context树,树的每个节点都可以有任意多个子节点

text
Context.Background()
 `-> ctx
     |- WithCancel(ctx) -> ctxWithCancel
     |- WithDeadline(ctx, t) -> ctxWithDeadline
     |- WithTimeout(ctx, t) -> ctxWithTimeout
     `- WithValue(ctx, k, v) -> ctxWithValue
                                 `-> WithXxx(ctxWithValue, xxx) -> ctxXxx

三、使用

go
// 返回 Context 的截止时间和一个布尔值,表示是否设置了截止时间
deadline, status := ctx.Deadline()
if status {
    fmt.Println("Deadline set:", deadline)
} else {
    fmt.Println("No deadline set")
}
go
// 返回一个通道,当 Context 被取消或超时时,该通道会被关闭,返回{}
select {
case <-ctx.Done():  // 超时取消后返回{}得到响应
    fmt.Println("Context done:", ctx.Err())
case <-time.After(5 * time.Second):
    fmt.Println("Operation completed")
}
go
// 返回 Context 结束的原因,如果 Context 还未结束,返回 nil
if err := ctx.Err(); err != nil {
    fmt.Println("Context error:", err)
}
go
// 返回与 Context 关联的键对应的值,如果没有对应的值,返回 nil
userID := ctx.Value("userID").(int)
fmt.Printf("User ID: %d\n", userID)

四、自定义

Context本质是一个接口,其定义了四个方法,实现该接口即可自定义Context

go
type Context interface {
    Deadline() (deadline time.Time, ok bool)  // 自动取消或超时取消后返回
    Done() <-chan struct{}                    // 当Context被取消或关闭后,返回一个被关闭的channel
    Err() error                               // 当Context被取消或关闭后,返回context取消的原因
    Value(key interface{}) interface{}        // 获取设置的key对应的值
}