首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

上下文 | context

  • import "context"
  • 概述
  • 索引
  • 示例

概述

Context 包定义了上下文类型,该上下文类型跨越 API 边界和进程之间传递截止期限,取消信号和其他请求范围值。

对服务器的传入请求应创建一个 Context,对服务器的传出调用应接受 Context。它们之间的函数调用链必须传播 Context,可以用使用 WithCancel,WithDeadline,WithTimeout 或WithValue创建的派生上下文替换。当 Context 被取消时,从它派生的所有 Context 也被取消。

WithCancel,WithDeadline 和 WithTimeout 函数采用Context(父级)并返回派生的Context(子级)和CancelFunc。调用 CancelFunc 将取消子对象及其子对象,删除父对子对象的引用,并停止任何关联的定时器。未能调用CancelFunc 会泄漏子项及其子项,直至父项被取消或计时器激发。go vet 工具检查在所有控制流路径上使用 CancelFuncs。

使用 Contexts 的程序应该遵循这些规则来保持包之间的接口一致,并使静态分析工具能够检查上下文传播:

不要将上下文存储在结构类型中;相反,将一个 Context 明确地传递给每个需要它的函数。上下文应该是第一个参数,通常命名为 ctx:

代码语言:javascript
复制
func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}

即使函数允许,也不要传递nil Context。如果您不确定要使用哪个Context,请传递 context.TODO。

使用上下文值仅适用于传输进程和 API 的请求范围数据,而不用于将可选参数传递给函数。

相同的上下文可以传递给在不同 goroutine 中运行的函数; 上下文对于多个 goroutine 同时使用是安全的。

有关使用Contexts 的服务器的示例代码,请参阅https://blog.golang.org/context

索引

Variables(变量)

type CancelFunc

type Context

  • func Background() Context
  • func TODO() Context
  • func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
  • func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
  • func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
  • func WithValue(parent Context, key, val interface{}) Context

示例

WithCancel WithDeadline WithTimeout WithValue

包文件

变量

Canceled 是上下文取消时 Context.Err 返回的错误。

代码语言:javascript
复制
var Canceled = errors.New("context canceled")

DeadlineExceeded 是 Context.Err 在上下文截止时间过后返回的错误。

代码语言:javascript
复制
var DeadlineExceeded error = deadlineExceededError{}

type CancelFunc(查看源代码)

CancelFunc通知操作放弃其工作。CancelFunc不会等待工作停止。在第一次调用之后,对CancelFunc的后续调用不起作用。

代码语言:javascript
复制
type CancelFunc func()

上下文包含截止日期,取消信号以及跨越 API 边界的其他值。

上下文的方法可能会被多个 goroutine 同时调用。

代码语言:txt
复制
type Context interface {
        // 截止日期返回代表此情境完成工作的时间
        // 应该取消。 如果没有截止日期,截止日期返回 ok == false
        // 组,对Deadline的连续调用返回相同的结果。
        Deadline() (deadline time.Time, ok bool)

        // 完成后,完成返回一个关闭的频道
        // 上下文应该取消。 如果可以的话,完成可能返回 nil(零)
        // 永远不会被取消。 连续调用完成返回相同的值。
        //
        // WithCancel 安排在完成取消时关闭完成;
        // WithDeadline 安排完成时间截止
        // 到期; WithTimeout 安排在完成超时时关闭
        // 经过。
        //
        // Done 提供在select语句中使用:
        //
        //  // Stream 使用 DoSomething 生成值并将它们发送出去
        //  // 直到 DoSomething 返回一个错误或者 ctx.Done 关闭。
        //  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:
        //  		}
        //  	}
        //  }
        //
        // 有关如何使用的更多示例,请参阅https://blog.golang.org/pipelines
        // 一个Done 通道取消。
        Done() <-chan struct{}

        // 如果Done 尚未关闭,则Err返回nil。
        // 如果完成关闭,则 Err 会返回一个非零错误,以解释原因:
        // 如果上下文被取消,则取消
        // 或者如果上下文的截止时间已过,则截止时间超过。
        // 在Err 返回非零错误之后,对Err 的连续调用返回相同的错误。
        Err() error

        // 值返回与此上下文关联的值相关的值,或者 nil
        // 如果没有值与键关联。 Successive 调用Value
        // 相同的密钥返回相同的结果。
        //
        // 仅将上下文值用于传输的请求范围数据
        // 进程和API边界,而不是将可选参数传递给
        // 函数。
        //
        // 一个关键字标识 Context 中的特定值。 希望的Function
        // 在Context中存储值通常在全局中分配一个键
        // 然后使用该键作为 context.WithValue 和的参数
        // Context.Value, 一个密钥可以是支持平等的任何类型;
        // 包应该将键定义为未导出的类型以避免
        // 碰撞
        //
        // 定义Context键的软件包应提供类型安全的访问器
        // 对于使用该键存储的值:
        //
        
        // 	pac包用户定义了存储在Contexts输入中的用户类型。
        //
        // 	import "context"
        //
        // 	// 包用户定义了存储在上下文中的用户类型。包用户定义了存储在上下文中的用户类型。
        // 	type User struct {...}
        //
        // 	// key(键)是此软件包中定义的键的未导出类型。
        // 	// 这可以防止与其他软件包中定义的键的冲突。
        // 	键入关键字int
        //
        // 	// userKey是用户的关键。上下文中的用户值。 它是
        // 	// 未导出; 客户端使用user.NewContext和user.FromContext
        // 	// 而不是直接使用此密钥。
        // 	var userKey key = 0
        //
        // 	// NewContext 返回一个带有值u的新Context。
        // 	func NewContext(ctx context.Context, u *User) context.Context {
        // 		return context.WithValue(ctx, userKey, u)
        // 	}
        //
        // 	// FromContext返回存储在ctx中的User值(如果有)。
        // 	func FromContext(ctx context.Context) (*User, bool) {
        // 		u, ok := ctx.Value(userKey).(*User)
        // 		返回 u, ok
        // 	}
        Value(key interface{}) interface{}
}

func Background(查看源代码)

代码语言:javascript
复制
func Background() Context

背景返回non-nil(非零),空的 Context。它从未被取消,没有值,也没有最后期限。它通常由主函数,初始化和测试使用,并作为传入请求的top-level Context (顶级上下文)。

代码语言:javascript
复制
func TODO() Context

TODO 返回非零空的上下文。代码应该使用context.TODO,当它不清楚使用哪个 Context或它尚不可用时(因为周围的函数尚未扩展为接受Context参数)。TODO 被静态分析工具识别,以确定上下文是否在程序中正确传播。

func WithCancel(查看源代码)

代码语言:javascript
复制
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel 返回一个新的完成通道的父级的副本。返回的上下文的 Done 通道在返回的取消函数被调用时或父上下文的 Done 通道关闭时关闭,无论哪个先发生。

取消这个上下文会释放与它相关的资源,所以只要完成在这个Context 中运行的操作,代码就应该调用 cancel。

示例

此示例演示使用可取消上下文来防止 goroutine 泄漏。在示例函数结束时,由 gen 开始的 goroutine 将返回而不会泄漏。

代码语言:txt
复制
package main

import (
	"context"
	"fmt"
)

func main() {
	// gen在单独的goroutine中生成整数
	// 将它们发送到返回的频道。
	// gen的调用者需要取消一次该上下文
	// 他们完成消耗生成的整数不泄漏
	// 内部goroutine由gen开始。
	gen := func(ctx context.Context) <-chan int {
		dst := make(chan int)
		n := 1
		go func() {
			for {
				select {
				case <-ctx.Done():
					return // 返回不要泄漏goroutine
				case dst <- n:
					n++
				}
			}
		}()
		return dst
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // 当我们完成消耗整数时取消

	for n := range gen(ctx) {
		fmt.Println(n)
		if n == 5 {
			break
		}
	}
}

func WithDeadline(查看源代码)

代码语言:javascript
复制
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)

WithDeadline 返回父上下文的副本,并将截止日期调整为不晚于d。如果父母的截止日期早于d,WithDeadline(parent,d)在语义上等同于父母。当截止日期到期,返回的取消功能被调用时,或者父上下文的完成通道关闭时,返回的上下文的完成通道将关闭,以先发生者为准。

取消这个上下文会释放与它相关的资源,所以只要完成在这个Context 中运行的操作,代码就应该调用cancel。

示例

这个例子传递一个任意期限的上下文来告诉阻塞函数,它应该尽快放弃它的工作。

代码语言:txt
复制
package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	d := time.Now().Add(50 * time.Millisecond)
	ctx, cancel := context.WithDeadline(context.Background(), d)

	// 即使ctx过期,最好还是调用
	// 取消功能无论如何。 如果不这样做可能会保留
	// 上下文及其父级的活动时间超过必要时间
	defer cancel()

	select {
	case <-time.After(1 * time.Second):
		fmt.Println("overslept")
	case <-ctx.Done():
		fmt.Println(ctx.Err())
	}

}

func WithTimeout(查看源代码)

代码语言:javascript
复制
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout 返回 WithDeadline(parent, time.Now().Add(timeout)) 。

取消这个上下文可以释放与它相关的资源,因此只要在这个Context 中运行的操作完成,代码就应该立即调用 cancel:

代码语言:txt
复制
func slowOperationWithTimeout(ctx context.Context) (Result, error) {
	ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
	defer cancel()  // 如果slowOperation在超时之前完成,则释放资源
	return slowOperation(ctx)
}

示例

这个例子传递一个带有超时的上下文来告诉阻塞函数,它应该在超时过后放弃它的工作。

代码语言:txt
复制
package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	// 传递带超时的上下文告诉阻塞函数
	// 超时过后应该放弃它的工作。
	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
	defer cancel()

	select {
	case <-time.After(1 * time.Second):
		fmt.Println("overslept")
	case <-ctx.Done():
		fmt.Println(ctx.Err()) // 打印"context deadline exceeded"
	}

}

func WithValue(查看源代码)

代码语言:javascript
复制
func WithValue(parent Context, key, val interface{}) Context

WithValue 返回父键的副本,其中与键关联的值是val。

使用上下文值仅适用于传输进程和 API 的请求范围数据,而不用于将可选参数传递给函数。

提供的密钥必须具有可比性,不应该是字符串类型或任何其他内置类型,以避免使用上下文的包之间发生冲突。WithValue 的用户应该为键定义他们自己的类型。为了避免在分配给接口时分配{},上下文键通常具有具体类型 struct {}。或者,导出的上下文关键字变量的静态类型应该是指针或接口。

示例

代码语言:javascript
复制
package main

import (
	"context"
	"fmt"
)

func main() {
	type favContextKey string

	f := func(ctx context.Context, k favContextKey) {
		if v := ctx.Value(k); v != nil {
			fmt.Println("found value:", v)
			return
		}
		fmt.Println("key not found:", k)
	}

	k := favContextKey("language")
	ctx := context.WithValue(context.Background(), k, "Go")

	f(ctx, k)
	f(ctx, favContextKey("color"))

}

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com