Golang runtime -- context package / context Context

Context

// A Context carries a deadline, a cancellation signal, and other values across
// API boundaries.
// Context包含一个截止日期、一个取消信号和跨越API边界的其他值。

// Context's methods may be called by multiple goroutines simultaneously.
// Context的方法可以被多个goroutine同时调用。
type Context interface {
    
    
	// Deadline returns the time when work done on behalf of this context
	// should be canceled. Deadline returns ok==false when no deadline is
	// set. Successive calls to Deadline return the same results.
	
	// Deadline返回在此context下做完工作应该取消的时间。当没有设置截止日
	// 期时,Deadline返回ok==false。连续调用Deadline返回相同的结果。
	Deadline() (deadline time.Time, ok bool)


	// Done返回一个通道,当在该上下文所做的工作应该取消时,该通道关闭。如果这
	// 个上下文永远不能取消,Done可能返回nil。对Done的连续调用返回相同的值。
	// Done通道的关闭可以在cancel函数返回之后异步发生

	// WithCancel安排Done在cancel被调用时关闭;WithDeadline安排Done在截止
	// 日期到期时关闭;WithTimeout设置Done在超时后关闭。

	// Done提供给select语句使用:
	
	//  // Stream generates values with DoSomething and sends them to out
	//  // until DoSomething returns an error or ctx.Done is closed.
	//  func Stream(ctx context.Context, out chan<- Value) error {
    
    
	//  	for {
    
    
	//  		v, err := DoSomething(ctx)
	//  		if err != nil {
    
    
	//  			return err
	//  		}
	//  		select {
    
    
	//  		case <-ctx.Done():
	//  			return ctx.Err()
	//  		case out <- v:
	//  		}
	//  	}
	//  }
	//
	// See https://blog.golang.org/pipelines for more examples of how to use
	// a Done channel for cancellation.
	Done() <-chan struct{
    
    }

	// If Done is not yet closed, Err returns nil.
	// If Done is closed, Err returns a non-nil error explaining why:
	// Canceled if the context was canceled
	// or DeadlineExceeded if the context's deadline passed.
	// After Err returns a non-nil error, successive calls to Err return the same error.
	Err() error

	// Value返回与上下文相关的key的值,如果没有与key相关的值则返回nil。连
	// 续调用具有相同键的Value返回相同的结果。

	// 仅对传输到进程和API边界的请求范围内的数据使用上下文值,而不是将可选参
	// 数传递给函数。

	// key 标识上下文中的特定值。希望在Context中存储值的函数通常在全局变量
	// 中分配一个键,可以使用该键作为context.WithValue和
	// Context.Value的参数。 key 可以是支持相等的任何类型;包应该将key 定义
	// 为未导出的类型,以避免冲突。

	// 定义Context 键的包应该为使用该键存储的值提供类型安全的访问器:
	
	// 	// Package user defines a User type that's stored in Contexts.
	// 	package user
	//
	// 	import "context"
	//
	// 	// User is the type of value stored in the Contexts.
	// 	type User struct {...}
	//
	// 	// key is an unexported type for keys defined in this package.
	// 	// This prevents collisions with keys defined in other packages.
	// 	type key int
	//
	// 	// userKey is the key for user.User values in Contexts. It is
	// 	// unexported; clients use user.NewContext and user.FromContext
	// 	// instead of using this key directly.
	// 	var userKey key
	//
	// 	// NewContext returns a new Context that carries value u.
	// 	func NewContext(ctx context.Context, u *User) context.Context {
    
    
	// 		return context.WithValue(ctx, userKey, u)
	// 	}
	//
	// 	// FromContext returns the User value stored in ctx, if any.
	// 	func FromContext(ctx context.Context) (*User, bool) {
    
    
	// 		u, ok := ctx.Value(userKey).(*User)
	// 		return u, ok
	// 	}
	Value(key any) any
}

type

emptyCtx

// emptyCtx永远不会取消,没有值,也没有截止日期。它不是struct{},因为这
// 种类型的变量必须有不同的地址。
type emptyCtx int

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
}

func (e *emptyCtx) String() string {
    
    
	switch e {
    
    
	case background:
		return "context.Background"
	case todo:
		return "context.TODO"
	}
	return "unknown empty Context"
}

cancelCtx

// cancelCtx可以被取消。当取消时,它还取消实现canceler的所有子对象。
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
}

canceller interface

// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
    
    
	cancel(removeFromParent bool, err, cause error)
	Done() <-chan struct{
    
    }
}

timerCtx

// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
    
    
	*cancelCtx
	timer *time.Timer // Under cancelCtx.mu.

	deadline time.Time
}

func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
    
    
	return c.deadline, true
}

func (c *timerCtx) String() string {
    
    
	return contextName(c.cancelCtx.Context) + ".WithDeadline(" +
		c.deadline.String() + " [" +
		time.Until(c.deadline).String() + "])"
}

func (c *timerCtx) cancel(removeFromParent bool, err, cause error) {
    
    
	c.cancelCtx.cancel(false, err, cause)
	if removeFromParent {
    
    
		// Remove this timerCtx from its parent cancelCtx's children.
		removeChild(c.cancelCtx.Context, c)
	}
	c.mu.Lock()
	if c.timer != nil {
    
    
		c.timer.Stop()
		c.timer = nil
	}
	c.mu.Unlock()
}

valueCtx

// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
    
    
	Context
	key, val any
}

func (c *valueCtx) String() string {
    
    
	return contextName(c.Context) + ".WithValue(type " +
		reflectlite.TypeOf(c.key).String() +
		", val " + stringify(c.val) + ")"
}

func (c *valueCtx) Value(key any) any {
    
    
	if c.key == key {
    
    
		return c.val
	}
	return value(c.Context, key)
}

function

default context

contextThe most commonly used methods in the package are context.Background, context.TODO, both of which return pre-initialized private variables backgroundand todo, which can be reused in the same Go program:

Background()

// Background返回一个非nil的空Context。它永远不会被取消,没有values,也没
// 有最后期限。它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文。
func Background() Context

TODO()

// TODO返回一个非nil的空Context。代码应该使用context.TODO,当不清楚要使用哪个
// 上下文或它还不可用时(因为周围的函数还没有扩展到接受上下文参数)。
func TODO() Context

Context Hierarchy
Context Hierarchy

We can understand context.Contexthow . In this code, we create a context with an expiration time of 1s, and pass handlethe function , which will use 500msthe time to process the incoming request:

func main() {
    
    
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	go handle(ctx, 500*time.Millisecond)
	select {
    
    
	case <-ctx.Done():
		fmt.Println("main", ctx.Err())
	}
}

func handle(ctx context.Context, duration time.Duration) {
    
    
	select {
    
    
	case <-ctx.Done():
		fmt.Println("handle", ctx.Err())
	case <-time.After(duration):
		fmt.Println("process request with", duration)
	}
}

Because the expiration time is greater than the processing time, we have enough time to process the request. Running the above code will print out the following:

$ go run context.go
process request with 500ms
main context deadline exceeded

handleThe function does not enter the timeout selectbranch, but the select of the main function will wait for context.Contextthe timeout and print out main context deadline exceeded.

If we increase the request processing time to 1500ms, the entire program will be terminated due to the expiration of the context:

$ go run context.go
main context deadline exceeded
handle context deadline exceeded

I believe these two examples can help readers context.Contextunderstand the usage and design principles of - Multiple Goroutines subscribe ctx.Done()to the messages in the pipeline at the same time, and once they receive a cancellation signal, they will immediately stop the currently executing work.

cancel signal

context.WithCancelA function that can derive a new subcontext context.Contextfrom and return a function that cancels that context. Once we execute the returned cancel function, the current context and its subcontexts will be canceled, and all Goroutines will receive this cancellation signal synchronously.

insert image description here

WithCancel()

// WithCancel返回父对象的副本, 带有一个新的Done通道。当返回的cancel函数被调
// 用或父上下文的Done通道被关闭时,返回上下文的Done通道被关闭,以先发生的
// 情况为准。
// 取消此上下文将释放与之关联的资源,因此代码应该在此上下文中运行的操作完
// 成后立即调用cancel。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) 

In context.WithCanceladdition to , contexttwo other functions context.WithDeadline and context.WithTimeoutcan also create timer contexts that can be canceled context.timerCtx:

context.propagateCancel The function of is to synchronize the cancel and end signals between the parent and the child , to ensure that when the parent is canceled, the child will also receive the corresponding signal, and there will be no inconsistent state.

In context.WithCanceladdition to , context two other functions context.WithDeadline and context.WithTimeout can also create timer contexts that can be canceled context.timerCtx:

WithTimeout()

// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout))
// 取消此上下文将释放与之相关的资源,因此代码应该在此上下文中运行的操作完
// 成后立即调用cancel:

//	func slowOperationWithTimeout(ctx context.Context) (Result, error) {
    
    
//		ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
//		defer cancel()  // releases resources if slowOperation completes before timeout elapses
//		return slowOperation(ctx)
//	}
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithDeadline()

// WithDeadline返回父上下文的副本,其截止日期调整为不晚于d。如果父上下文
// 的截止日期已经早于d, WithDeadline(parent, d)在语义上等同于父上下
// 文。返回的上下文的Done通道在截止日期到期、返回的cancel函数被调用或父上下
// 文的Done通道被关闭时关闭,以先发生者为准。

// 取消此上下文将释放与之关联的资源,因此代码应该在此上下文中运行的操作完成
// 后立即调用cancel。
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

pass-by-value method

Finally, we need to understand how to use context to pass values. The context in the package context.WithValuecan create a child context from the parent context, and the value-passed child context uses context.valueCtx the type:

WithValue()

// WithValue returns a copy of parent in which the value associated with key is
// val.

// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
// 提供的键必须具有可比性,并且不应该是字符串类型或任何其他内置类型,以避免
// 使用上下文的包之间发生冲突。WithValue的用户应该为键定义自己的类型。为
// 了避免在分配interface{}时分配,上下文键通常有具体的struct{}。或者,导出的
// 上下文关键变量的静态类型应该是指针或接口。
func WithValue(parent Context, key, val any) Context 

Guess you like

Origin blog.csdn.net/chinusyan/article/details/129944339