golang,go,博客,开源,编程
在 Go 语言中,context
包提供了一种在多个 goroutine 之间传递取消信号和请求作用域(如超时、截止时间等)的机制。context
在并发编程中扮演着重要角色,尤其是在处理 HTTP 请求、数据库操作等需要在多个 goroutine 间共享和传递状态的场景中。
context
的用途context
的基本概念context
包中的主要概念是 Context
类型,它是一个接口,定义了用于取消信号、截止时间、超时和传递数据的方法。Context
实现是不可变的,每次创建新 Context 时会基于现有的 Context 扩展出新的状态。
context
的常见函数和方法context.Background()
:返回一个空的、根 Context,通常用于整个程序的入口。context.TODO()
:返回一个空的 Context,通常用于尚未决定具体实现时。context.WithCancel(parent Context)
:基于 parent
Context 创建一个新的 Context,并返回一个取消函数。调用该函数会取消这个新创建的 Context。context.WithDeadline(parent Context, deadline time.Time)
:基于 parent
Context 创建一个新的 Context,并设置一个截止时间,超过该时间会自动取消。context.WithTimeout(parent Context, timeout time.Duration)
:基于 parent
Context 创建一个新的 Context,并设置一个超时限制,超时后会自动取消。context.WithValue(parent Context, key, value interface{})
:基于 parent
Context 创建一个新的 Context,用于传递数据(key
和 value
)。Context
方法Done()
:返回一个 channel,当 Context 被取消时,该 channel 会被关闭,指示相关的工作可以停止。Err()
:返回 Context 被取消时的错误信息,如果取消信号被发送,则返回 context.Canceled
或 context.DeadlineExceeded
。Value(key interface{})
:获取与 key
相关联的值,通常用于传递请求上下文中的数据。package main
import (
"context"
"fmt"
"time"
)
func doWork(ctx context.Context) {
select {
case <-time.After(5 * time.Second): // 模拟执行任务
fmt.Println("Task completed.")
case <-ctx.Done(): // 接收到取消信号
fmt.Println("Task cancelled:", ctx.Err())
}
}
func main() {
// 创建一个带有超时限制的 Context
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// 启动一个 goroutine执行任务
go doWork(ctx)
// 等待任务完成
time.Sleep(6 * time.Second)
}
context.WithTimeout
:创建一个超时的 Context,超过 3 秒后,Context 会自动取消。select
语句监听两个 channel:一个是 time.After
,模拟任务的执行时间,另一个是 ctx.Done()
,用于监听超时或取消信号。输出:
Task cancelled: context deadline exceeded
package main
import (
"context"
"fmt"
)
func processRequest(ctx context.Context) {
// 从 Context 中获取请求 ID
reqID := ctx.Value("request_id")
fmt.Printf("Processing request with ID: %v\n", reqID)
}
func main() {
// 创建一个带有值的 Context
ctx := context.WithValue(context.Background(), "request_id", "123456")
// 传递 Context 给 goroutine
processRequest(ctx)
}
输出:
Processing request with ID: 123456
context.WithValue
:用于在 Context 中传递数据,这里传递了一个键为 "request_id"
的值 "123456"
。ctx.Value("request_id")
获取传递的数据。Context
作为函数的第一个参数。大多数标准库函数都是如此做的,Go 社区也建议这样做。它应该用于传递请求上下文信息。WithValue
:WithValue
主要用于传递轻量级的数据,如请求 ID、认证信息等。过度使用 WithValue
会使得代码复杂化,难以管理。Context
:对于可能需要取消的操作或需要超时控制的任务,使用 Context
来传递取消信号。select
和 Done()
检测取消信号:通过 ctx.Done()
可以检测到 Context 的取消信号,及时停止工作,避免资源浪费。context
?Context
与 CancelFunc
的关系每个通过 context.WithCancel
、context.WithDeadline
或 context.WithTimeout
创建的 Context
都会返回一个取消函数 (CancelFunc
)。该函数用于触发取消操作。例如,调用 cancel()
会使得与该 Context
相关的 Done()
channel 被关闭,从而触发取消信号。
ctx, cancel := context.WithCancel(context.Background())
// 在需要的时候调用 cancel 来取消 Context
cancel()
context
包提供了在多个 goroutine 中传递取消信号、超时限制和请求范围数据的功能,是并发编程中的重要工具。context
可以实现任务的取消、超时控制,以及在函数间传递共享的请求信息。context
对象本身是不可变的,每次操作都会返回一个新的 Context
对象。