这篇文章主要介绍了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.Background、
context.TODO,这两个方法都会返回预先初始化好的私有变量
background和
todo,它们会在同一个 Go 程序中被复用:
Background()
// Background返回一个非nil的空Context。它永远不会被取消,没有values,也没 // 有最后期限。它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文。 func Background() Context
TODO()
// TODO返回一个非nil的空Context。代码应该使用context.TODO,当不清楚要使用哪个 // 上下文或它还不可用时(因为周围的函数还没有扩展到接受上下文参数)。 func TODO() 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的使用方法和设计原理 — 多个 Goroutine 同时订阅
ctx.Done()管道中的消息,一旦接收到取消信号就立刻停止当前正在执行的工作。
取消信号
context.WithCancel函数能够从
context.Context中衍生出一个新的子上下文并返回用于取消该上下文的函数。一旦我们执行返回的取消函数,当前上下文以及它的子上下文都会被取消,所有的 Goroutine 都会同步收到这一取消信号。
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