温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Golang上下文Context的常见应用场景是什么

发布时间:2023-04-15 14:37:04 来源:亿速云 阅读:103 作者:iii 栏目:开发技术

这篇文章主要介绍了Golang上下文Context的常见应用场景是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Golang上下文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
}

类型

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
}

canceler接口

// 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)
}

函数

默认上下文

context 包中最常用的方法还是 context.Backgroundcontext.TODO,这两个方法都会返回预先初始化好的私有变量 backgroundtodo,它们会在同一个 Go 程序中被复用:

Background()

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

TODO()

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

Golang上下文Context的常见应用场景是什么

Context 层级关系

我们可以通过一个代码片段了解 context.Context 是如何对信号进行同步的。在这段代码中,我们创建了一个过期时间为 1s 的上下文,并向上下文传入 handle 函数,该方法会使用 500ms 的时间处理传入的请求:

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

因为过期时间大于处理时间,所以我们有足够的时间处理该请求,运行上述代码会打印出下面的内容:

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

handle 函数没有进入超时的 select 分支,但是 main 函数的 select 却会等待 context.Context 超时并打印出main context deadline exceeded

如果我们将处理请求时间增加至 1500ms,整个程序都会因为上下文的过期而被中止,:

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

相信这两个例子能够帮助各位读者理解 context.Context 的使用方法和设计原理 &mdash; 多个 Goroutine 同时订阅 ctx.Done() 管道中的消息,一旦接收到取消信号就立刻停止当前正在执行的工作。

取消信号

context.WithCancel 函数能够从 context.Context 中衍生出一个新的子上下文并返回用于取消该上下文的函数。一旦我们执行返回的取消函数,当前上下文以及它的子上下文都会被取消,所有的 Goroutine 都会同步收到这一取消信号。

Golang上下文Context的常见应用场景是什么

WithCancel()

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

除了 context.WithCancel 之外,context 包中的另外两个函数 context.WithDeadline context.WithTimeout 也都能创建可以被取消的计时器上下文 context.timerCtx

context.propagateCancel 的作用是在 parent 和 child 之间同步取消和结束的信号,保证在 parent 被取消时,child 也会收到对应的信号,不会出现状态不一致的情况。

除了 context.WithCancel 之外,context 包中的另外两个函数 context.WithDeadline context.WithTimeout 也都能创建可以被取消的计时器上下文 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)

传值方法

在最后我们需要了解如何使用上下文传值,context 包中的 context.WithValue 能从父上下文中创建一个子上下文,传值的子上下文使用 context.valueCtx 类型:

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

关于“Golang上下文Context的常见应用场景是什么”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Golang上下文Context的常见应用场景是什么”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI