上下文 | 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:

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

包文件

变量

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

var Canceled = errors.New("context canceled")

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

var DeadlineExceeded error = deadlineExceededError{}

type CancelFunc(查看源代码)

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

type CancelFunc func()

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

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

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(查看源代码)

func Background() Context

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

func TODO() Context

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

func WithCancel(查看源代码)

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

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

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

示例

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

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(查看源代码)

func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)

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

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

示例

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

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(查看源代码)

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

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

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

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

示例

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

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(查看源代码)

func WithValue(parent Context, key, val interface{}) Context

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

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

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

示例

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

}

扫码关注云+社区

领取腾讯云代金券