context
context.Context 是一个接口
主要功能:
- 取消控制(Cancel)
- 超时 / 截止时间(Timeout / Deadline)
- 携带数据(Value)
- 统一管理多层 goroutine 生命周期
创建
| 函数 | 功能 |
|---|---|
| context.Background() | 空 context,通常作为根 context 使用 |
| context.TODO() | 占位 context,表示还没决定使用哪种 context |
| context.WithCancel(parent) | 基于父 context 创建可取消 context,返回 ctx, cancel() |
| context.WithTimeout(parent, duration) | 自动在 duration 后取消 |
| context.WithDeadline(parent, time) | 在指定时间点取消,类似 WithTimeout |
| context.WithValue(parent, key, value) | 绑定 key-value 数据,适用于请求作用域 |
context.Background() 和 context.TODO() 只是一个起点,什么都没有。
WithCancel / WithTimeout / WithDeadline 会基于 parent 创建新的 context,但是会继承 deadline、取消信号。
父可以影响子,但是子不会触发父
type cancelCtx struct {
Context // 嵌入父 context
done chan struct{}
mu sync.Mutex
children map[canceler]struct{}
}- child 保存 parent 的引用
- parent 的 children 里注册 child
所以是双向关系,但是是单向影响。
主动取消
go
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("goroutine exit:", ctx.Err())
return
default:
fmt.Println("working...")
time.Sleep(500 * time.Millisecond) // 没有这个 Sleep,会密密麻麻一直打印
}
}
}()
time.Sleep(2 * time.Second)
cancel() // 主动取消
time.Sleep(1 * time.Second)超时
go
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-time.After(3 * time.Second):
fmt.Println("Finished work")
case <-ctx.Done():
fmt.Println("Timeout:", ctx.Err())
}“Finished work” 不会被打印
父有 timeout,子并不会有。但是父 Done,子也会 Done,所以虽然没有,但是却有好像有,只是实际上没有。
DDL
接受一个固定时间, 然后到了时间 Done
Value
Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it.
value 只是请求级别的横切信息,不是万能袋。
只能存kv,是链接结构。子可以覆盖父,子也能访问到父到value。