第五章 上下文 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对应的值
}