什么是 context
?
context
是 Go 标准库中用来管理任务生命周期和跨 API 数据传递的工具。它的主要应用场景是在并发编程中,尤其是处理像 HTTP 请求这样有超时限制或需要手动取消的任务。
为了更通俗地理解,可以把 context
想象成一个任务的「管理员」,它可以:
- 通知任务何时结束(比如超时或取消时)。
- 传递一些全局信息(比如用户身份或配置)。
从常用例子开始
1. HTTP 请求中的超时控制
假设我们正在写一个 HTTP 服务器,客户端请求要求超时 2 秒:
package main
import (
"context"
"fmt"
"net/http"
"time"
)
func handler(w http.ResponseWriter, r *http.Request) {
// 创建一个带超时的 context,2 秒后自动取消
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // 确保函数结束时释放资源
// 模拟一个耗时任务
select {
case <-time.After(3 * time.Second):
fmt.Fprintln(w, "Finished task")
case <-ctx.Done(): // 当 context 超时时,Done 会被触发
http.Error(w, "Request timed out", http.StatusRequestTimeout)
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
运行逻辑:
- 如果任务完成时间超过 2 秒,
ctx.Done()
会收到取消信号,及时停止任务,避免资源浪费。 context
帮助我们优雅地处理超时。
2. 跨 API 传递请求信息
context
还能传递数据,比如用户的认证信息或请求 ID:
package main
import (
"context"
"fmt"
)
func main() {
// 创建一个带键值对的 context
ctx := context.WithValue(context.Background(), "userID", 42)
// 传递 context 到其他函数
processRequest(ctx)
}
func processRequest(ctx context.Context) {
// 从 context 中取出数据
if userID := ctx.Value("userID"); userID != nil {
fmt.Println("Processing request for user:", userID)
} else {
fmt.Println("No user ID found")
}
}
运行逻辑:
- 主函数设置了
userID
,传递给processRequest
。 - 在子函数中,可以从
context
中提取出该数据,实现信息的跨 API 共享。
context
的关键功能
-
取消信号
使用context.WithCancel
来手动通知任务停止:ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(1 * time.Second) cancel() // 通知任务取消 }() <-ctx.Done() // 阻塞,直到取消信号被触发 fmt.Println("Task cancelled")
-
超时控制
使用context.WithTimeout
自动取消任务:ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() select { case <-time.After(3 * time.Second): fmt.Println("Task completed") case <-ctx.Done(): fmt.Println("Timeout:", ctx.Err()) // 输出超时错误 }
-
数据传递
使用context.WithValue
跨 API 传递数据。
为什么需要 context
?
-
解决并发中的控制问题:
在 Go 中,任务往往是通过goroutine
并发执行的,但如何优雅地终止任务?传统方法可能需要复杂的 channel 通信,而context
提供了一个统一的接口。 -
统一超时和取消机制:
无需手动编写超时逻辑或全局变量,context
内置了这些功能。 -
跨 API 共享数据:
context
可以像字典一样存储键值对,让函数间更方便地传递信息。
常用场景
-
HTTP 请求处理:
控制请求的超时、取消和数据传递。 -
数据库操作:
遇到慢查询时,设置超时或取消。 -
微服务通信:
在分布式系统中传递请求 ID 或认证信息。 -
并发任务协调:
例如多个 goroutine 需要根据同一个取消信号停止。
总结
通俗来说,context
是 Go 语言中用来管理任务生命周期和传递信息的工具,它让我们可以更方便地实现超时控制、手动取消和跨 API 数据共享,简化了并发编程中的复杂操作。