context 包在 Go 语言中用于管理请求范围内的元数据、控制并发操作的生命周期和设置超时等
context
的主要用途包括:
- 传递请求范围内的元数据:在处理一个请求的多个函数之间共享信息。
- 控制并发操作的生命周期:在多个 Goroutine 之间传递取消信号,控制并发操作的生命周期。
- 设置超时和截止时间:防止长期运行的操作,确保系统的响应能力。
Context 接口
Context
接口定义了四个方法:
-
Deadline() (deadline time.Time, ok bool):
返回 context 的截止时间。如果未设置截止时间,则
ok
返回false
。 -
Done() <-chan struct{}:
返回一个 channel,当 context 被取消或者超时时,该 channel 会被关闭。通常用于监听取消信号。
-
Err() error:
返回 context 被取消的原因(取消或超时)。如果 context 未被取消,则返回
nil
。 -
Value(key interface{}) interface{}:
返回与 key 相关联的值,通常用于在请求范围内传递请求特定的数据。
常用的 Context 类型和函数
-
context.Background()
:用作根 context,通常用于 main 函数、初始化和测试中。它是无法取消和无截止时间的基础 context。
-
context.TODO()
:用作占位符,表示尚未确定使用哪种 context 时的占位符。
-
context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
:创建一个可以取消的 context。返回的
cancel
函数用于关闭 context,通知相关 Goroutine 停止工作。
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 在适当的时机调用取消函数
-
context.WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)
:创建一个带有超时的 context。超过指定时间后,context 会被取消。
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // 在超时之前调用取消函数
-
context.WithDeadline(parent Context, deadline time.Time) (ctx Context, cancel CancelFunc)
:创建一个带有截止时间的 context。到达指定时间后,context 会被取消。
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Minute))
defer cancel() // 在截止时间之前调用取消函数
-
context.WithValue(parent Context, key, val interface{}) Context
:创建一个带有键值对的 context。用于在请求范围内传递请求特定的数据。
ctx := context.WithValue(context.Background(), "userID", 12345)
value := ctx.Value("userID")
具体使用
1. 取消信号
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
select {
case <-ctx.Done():
fmt.Println("Goroutine stopped")
}
}()
time.Sleep(1 * time.Second)
cancel() // 取消操作,通知 Goroutine 停止工作
time.Sleep(1 * time.Second)
}
2. 超时控制
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("operation timed out:", ctx.Err())
}
}
3. 传递请求域数据
package main
import (
"context"
"fmt"
)
func main() {
ctx := context.WithValue(context.Background(), "userID", 12345)
processRequest(ctx)
}
func processRequest(ctx context.Context) {
userID := ctx.Value("userID")
fmt.Println("User ID:", userID)
}
总结
context
包在 Go 中是处理并发操作和请求范围内的数据传递的一个强大工具。通过使用 context
包,我们可以更好地管理 goroutine 的生命周期,确保资源的有效利用和系统的健壮性。