概要
context,即上下文,用于在不同的goroutine之间同步请求特定的数据、取消信号以及处理请求的截止日期;对同一个context的操作是并发安全的
用途
1.传递数据
demo:
go
ctx := context.WithValue(context.Background(), KEY,NewRequestID())
v, ok := ctx.Value(k).(string)
if !ok{ return "" }
return v
2.统一取消
demo:
css
func main() {
ctx,cancel := context.WithCancel(context.Background())
go Speak(ctx)
time.Sleep(10*time.Second)
cancel()
time.Sleep(1*time.Second)
}
func Speak(ctx context.Context) {
for range time.Tick(time.Second){
select {
case <- ctx.Done():
fmt.Println("我要闭嘴了")
return
default:
fmt.Println("balabalabalabala")
}
}
}
3.超时处理 demo:
scss
func main() {
HttpHandler()
}
func NewContextWithTimeout() (context.Context,context.CancelFunc) {
return context.WithTimeout(context.Background(), 3 * time.Second)
}
func HttpHandler() {
ctx, cancel := NewContextWithTimeout()
defer cancel()
deal(ctx)
}
func deal(ctx context.Context) {
for i:=0; i< 10; i++ {
time.Sleep(1*time.Second)
select {
case <- ctx.Done():
fmt.Println(ctx.Err())
return
default:
fmt.Printf("deal time is %d\n", i)
}
}
}
注意: 统一取消和超时处理返回的cancel方法的执行是和管道触发通知的执行逻辑是一样的
底层原理
接口:
scss
type Context interface {
Deadline() (deadline time.Time, ok bool) // 是否设置了截止时间
Done() <-chan struct{} // 取消信号 channel,关闭表示已取消/超时
Err() error // 取消原因:`Canceled` / `DeadlineExceeded` / nil
Value(key any) any // 按 key 取值(请求级 metadata)
}
4种接口实现:
scss
Context (interface)
│
┌────────────────┼────────────────┬──────────────┐
│ │ │ │
emptyCtx cancelCtx timerCtx valueCtx
(Background/ (可取消) (超时/截止) (传值)
TODO)
empytCtx:
go
type emptyCtx int
var (
background = emptyCtx(0) // context.Background()
todo = emptyCtx(1) // context.TODO()
)
cancelCtx:
go
type cancelCtx struct {
Context // 嵌入 parent
mu sync.Mutex
done atomic.Value // 原子值,存贮chan struct{},懒创建
children map[canceler]struct{} // 子 context
err error // 取消原因
}
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := &cancelCtx{Context: parent}
propagateCancel(parent, c) // 把 c 挂到 parent 的 children
return c, func() { c.cancel(true, Canceled) }
}
func (c *cancelCtx) Done() <-chan struct{} {
d := c.done.Load()
if d != nil {
return d.(chan struct{})
}
c.mu.Lock()
defer c.mu.Unlock()
d = c.done.Load()
if d == nil {
d = make(chan struct{})
c.done.Store(d)
}
return d.(chan struct{})
}
1. 加锁
2. 若已取消 → 直接返回
3. close(done channel) ← 所有监听 Done 的 goroutine 被唤醒
4. err = Canceled 或 DeadlineExceeded
5. 递归 cancel 所有 children
6. 从 parent.children 里移除自己
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
c.mu.Lock()
if c.err != nil { /* 已取消 */ return }
c.err = err
close(c.done.Load().(chan struct{}))
for child := range c.children {
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
timerCtx:
lua
type timerCtx struct {
cancelCtx
timer *time.Timer
deadline time.Time
}
valueCtx:
arduino
type valueCtx struct {
Context
key, val any
}