Golang笔记 5.3 上下文 context

前言

golang 的 Context 包,是专门用来简化多个goroutine之间的上下文同步。

本篇笔记主要参考了 go blog

我正在学习酷酷的 Golang,可点此查看帖子Golang学习笔记汇总

1 库的介绍

Go 语言中的每一个请求的都是通过一个单独的 Goroutine 进行处理的,HTTP/RPC 请求的处理器往往都会启动新的 Goroutine 访问数据库和 RPC 服务,我们可能会创建多个 Goroutine 来处理一次请求,而 Context 的主要作用就是在不同的 Goroutine 之间同步请求特定的数据、同时优雅地结束上下文。

一个实际例子是,在Go服务器程序中,每个请求都会有一个goroutine去处理。然而,处理程序往往还需要创建额外的goroutine去访问后端资源,比如数据库、RPC服务等。由于这些goroutine都是在处理同一个请求,所以它们往往需要访问一些共享的资源,比如用户身份信息、认证token、请求截止时间等。而且如果请求超时或者被取消后,所有的goroutine都应该马上退出并且释放相关的资源。这种情况也需要用Context来为我们取消掉所有goroutine。

一句话:Context 包可以在不同的 Goroutine 之间同步请求数据,还能优雅地设置超时及信号来结束上下文。

2 用法

2.1 Context 定义

context 包的核心是 struct Context,声明如下:

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

2.2 根 Context

开始上下文的时候是以 Background() 作为最顶层的 parent context,然后再衍生出子context。context.Background() 只是一个空的上下文,没有什么特殊功能,它是上下文中最顶层的默认值.
这些 Context 对象形成一棵树:当一个 Context 对象被取消时,继承自它的所有 Context 都会被取消。两个实现如下:

var (
	background = new(emptyCtx)
	todo = new(emptyCtx)
)

func Background() Context {
	return background
}

func TODO() Context {
	return todo
}

在多数情况下如果函数没有上下文作为入参,我们往往都会使用 context.Background() 作为起始的 Context 向下传递。

例如:

  ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  defer cancel()
  r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

2.3 衍生的 Context (Derived contexts)

有了如上的父Context,那么是如何衍生更多的子 Context 的呢?这就要靠 context 包为我们提供的 With 系列的函数了。

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

通过这些函数,就创建了一颗Context树,树的每个节点都可以有任意多个子节点,节点层级可以有任意多个。

  • WithCancel函数,传递一个父Context作为参数,返回子Context,以及一个取消函数用来取消Context。
  • WithDeadline函数,和WithCancel差不多,它会多传递一个截止时间参数,意味着到了这个时间点,会自动取消Context,当然我们也可以不等到这个时候,可以提前通过取消函数进行取消。
  • WithTimeout和WithDeadline基本上一样,这个表示是超时自动取消,是多少时间后自动取消Context的意思。
  • WithValue函数和取消Context无关,它是为了生成一个绑定了一个键值对数据的Context,这个绑定的数据可以通过Context.Value方法访问到,这是我们实际用经常要用到的技巧,一般我们想要通过上下文来传递数据时,可以通过这个方法,如我们需要tarce追踪系统调用栈的时候。

例如上面的例子:

  ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  defer cancel()
  r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

context 会在例子所在的函数结束时调用 延迟函数 cancel() 来结束上下文,同时也可以通过超时来结束上下文。前者是请求成功回复后结束上下文,后者是请求超时未回复后结束上下文。

2.4 注意事项

Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx:

func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}

3 小结

GO 内置的 Context 包可以在不同的 Goroutine 之间同步请求数据,还能优雅地通过 WithTimeout 设置超时及 WithCancel 设置取消信号来结束上下文。

END


Golang 中,可以通过 `context` 包来获取上下文的所有信息。`context` 包提供了一个 `Context` 接口,该接口定义了一组方法,用于获取和设置上下文信息。具体来说,可以通过 `context.Background()` 方法创建一个空的上下文,然后使用 `WithCancel`、`WithDeadline`、`WithTimeout` 或 `WithValue` 等方法来创建一个新的上下文,并在其中设置相应的信息。 例如,以下代码演示了如何创建一个具有超时时间的上下文,并在其中设置一个字符串值: ``` package main import ( "context" "fmt" "time" ) func doSomething(ctx context.Context) { // 获取上下文中的字符串值 value := ctx.Value("key").(string) fmt.Println("value:", value) // 等待上下文超时或被取消 select { case <-ctx.Done(): fmt.Println("context done:", ctx.Err()) case <-time.After(5 * time.Second): fmt.Println("done") } } func main() { // 创建一个具有超时时间的上下文,并设置一个字符串值 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() ctx = context.WithValue(ctx, "key", "value") // 在新的 Goroutine 中执行任务 go doSomething(ctx) // 等待一段时间 time.Sleep(10 * time.Second) } ``` 在这个例子中,我们首先使用 `context.Background()` 方法创建了一个空的上下文,然后使用 `context.WithTimeout()` 方法创建了一个具有 3 秒超时时间的新上下文。接着,我们使用 `context.WithValue()` 方法在新上下文中设置了一个字符串值。最后,我们在新的 Goroutine 中执行了一个任务,并等待一段时间,以便让任务有足够的时间执行。在任务中,我们通过 `ctx.Value()` 方法获取了上下文中的字符串值,并使用 `select` 语句等待上下文超时或被取消。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值