Go package -- Context

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"
}

其中 emptyCtxtodoCtxbackgroundCtx的基础,实现了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函数返回带有一个新Donechannel的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) }
}

其中 返回的ccancelCtx

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中的Donechannel会被关闭。

可以从源码上看到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函数,则会将fctx解绑并返回true;如果当f已经运行或已经执行完成,则会返回false

Licensed under CC BY-NC-SA 4.0