Context包作为一个常用的Golang包,经常作为函数参数来控制多个Goroutine。本文整理该包中的函数,并鉴赏一下源码的实现😀
Go版本 1.22.1
函数
创建空Context
Backgroud
返回一个non-nil的空Context
,并且永远不会被cancel。通常作为请求的top-level 的Context
源码为:
1
2
3
| func Background() Context {
return backgroundCtx{}
}
|
其中结构体backgroundCtx
的定义为:
1
2
3
4
5
| type backgroundCtx struct{ emptyCtx }
func (backgroundCtx) String() string {
return "context.Background"
}
|
其中 emptyCtx
是todoCtx
与backgroundCtx
的基础,实现了Context
接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| type emptyCtx struct{}
func (emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (emptyCtx) Done() <-chan struct{} {
return nil
}
func (emptyCtx) Err() error {
return nil
}
func (emptyCtx) Value(key any) any {
return nil
}
|
TODO
与Backgroud()
类似,同样返回一个non-nil的空Context
。当不清楚使用哪个Context
或不可用时(附近的函数没有扩展为可接收Context
参数),使用TODO()
源码为:
1
2
3
| func TODO() Context {
return todoCtx{}
}
|
其中todoCtx
定义与backgroundCtx
类似,区别为作为显示字段有所区别:
1
2
3
4
5
| type todoCtx struct{ emptyCtx }
func (todoCtx) String() string {
return "context.TODO"
}
|
创建可取消的Context
WithCancel
WithCancel
函数返回带有一个新Done
channel的parent副本。当父Context 的Done
channel关闭或cancel
函数调用时,该Context中的Done
channel会被关闭。
1
2
3
4
| func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := withCancel(parent)
return c, func() { c.cancel(true, Canceled, nil) }
}
|
其中 返回的c
为cancelCtx
:
1
2
3
4
5
6
7
8
9
| type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done atomic.Value // of chan struct{}, created lazily, closed by first cancel call
children map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
cause error // set to non-nil by the first cancel call
}
|
Context
存放父Context,在本Context取消时可以从父Context中的children
删除Context。
WithCancelCause
WithCancelCause
类似于WithCancel
,但返回CancelCauseFunc
而不是CancelFunc
。在取消时可调用CancelCauseFunc
将自定义error保存到Context中,之后可以使用Cause
函数获取该错误。
1
2
3
4
| func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) {
c := withCancel(parent)
return c, func(cause error) { c.cancel(true, Canceled, cause) }
}
|
Cause
Cause
函数返回Context中的cause
,如果无cause则返回c.error
1
2
3
4
5
6
7
8
9
10
11
12
13
| func Cause(c Context) error {
if cc, ok := c.Value(&cancelCtxKey).(*cancelCtx); ok {
cc.mu.Lock()
defer cc.mu.Unlock()
return cc.cause
}
// There is no cancelCtxKey value, so we know that c is
// not a descendant of some Context created by WithCancelCause.
// Therefore, there is no specific cause to return.
// If this is not one of the standard Context types,
// it might still have an error even though it won't have a cause.
return c.Err()
}
|
创建带有过期时间的Conext
WithDeadline
1
2
3
| func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
return WithDeadlineCause(parent, d, nil)
}
|
如果时间晚于d
,Context中的Done
channel会被关闭。
可以从源码上看到WithDeadline
直接调用了WithDeadlineCause
函数。
WithDeadlineCause
1
| func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)
|
WithDeadlineCause
函数在WithDeadline
的基础上增加了cause
参数,用法与WithCancelCause
类似,在超过最后期限时返回cause
错误
WithTimeout
1
2
3
| func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
|
WithTimeout
调用WithDeadline(parent, time.Now().Add(timeout))
,在经过timeout
时间后关闭Done
channel。
WithTimeoutCause
1
2
3
| func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc) {
return WithDeadlineCause(parent, time.Now().Add(timeout), cause)
}
|
WithTimeoutCause
函数在WithTimeout
的基础上增加了cause
参数,用法与WithDeadlineCause
类似,在超过最后期限时返回cause
错误。
创建带有value的Context
WithValue
1
| func WithValue(parent Context, key, val any) Context
|
其中key
必须为可比较类型,并且不应该为string或其他内置类型,避免造成冲突。为了避免interface{}的内存分配,应使用自定义类型的struct{}
或 指针/接口
回调函数
AfterFunc
1
| func AfterFunc(ctx Context, f func()) (stop func() bool)
|
在ctx结束后,AfterFunc
会调用f
如果在回调前调用stop
函数,则会将f
与ctx
解绑并返回true
;如果当f
已经运行或已经执行完成,则会返回false