golang知识点--context

概述

context包定义了context的类型,该类型在api和进程之间传递deadline,取消信号和其他请求范围的值。
向服务器的传入请求应创建一个上下文,而对服务器的传出调用应接受一个上下文。 它们之间的函数调用链必须传播Context,可以选择将其替换为使用WithCancel,WithDeadline,WithTimeout或WithValue创建的派生Context。 取消上下文后,从该上下文派生的所有上下文也会被取消。
WithCancel,WithDeadline和WithTimeout函数采用Context(父级)并返回派生的Context(子级)和CancelFunc。 调用CancelFunc会取消该子代及其子代,删除父代对该子代的引用,并停止所有关联的计时器。 未能调用CancelFunc会使子代及其子代泄漏,直到父代被取消或计时器触发。 go vet检查所有控制流路径上是否都使用了CancelFuncs。
使用上下文的程序应遵循以下规则,以使各个包之间的接口保持一致,并启用静态分析工具来检查上下文传播:

  1. 不要将上下文存储在结构类型中; 而是将上下文明确传递给需要它的每个函数。 Context应该是第一个参数,通常命名为ctx:
func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}
  1. 即使函数允许,也不要传递nil Context。 如果不确定使用哪个上下文,请传递context.TODO。
  2. 仅将上下文值用于传递过程和API的请求范围的数据,而不用于将可选参数传递给函数。
  3. 可以将相同的上下文传递给在不同goroutine中运行的函数。 上下文对于由多个goroutine同时使用是安全的。

常用函数介绍

1.WithCancel
作用:返回一个父上下文的拷贝,并携带一个Done的管道。不论返回的cancel函数被调用或者当父上下文的Done channel关闭这两种情况哪一个先发生,返回的上下文的Done channel关闭。
取消此context回什邡预期关联的资源,因此在此上下文中运行的操作完成后,代码应立即调用cancel。

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

package main

import (
	"context"
	"fmt"
)

func main() {
	// gen generates integers in a separate goroutine and
	// sends them to the returned channel.
	// The callers of gen need to cancel the context once
	// they are done consuming generated integers not to leak
	// the internal goroutine started by gen.
	gen := func(ctx context.Context) <-chan int {
		dst := make(chan int)
		n := 1
		go func() {
			for {
				select {
				case <-ctx.Done():
					return // returning not to leak the goroutine
				case dst <- n:
					n++
				}
			}
		}()
		return dst
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // cancel when we are finished consuming integers

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

2.WithDeadline
WithDeadline返回父上下文的副本,并将截止日期调整为不迟于d(d是由我们定义的一个时间点,此处是与WithTimeout区别的地方),当父运行时间少于d,则本质上等于父上下文,当返回调用的cancel函数或者关闭父上下文的Done通道在截止时期之后,则关闭返回的上下文的Done通道。

该示例传递一个带有任意截止日期的上下文,以告知阻塞函数它应在到达工作后立即放弃工作。

package main

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

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

	// Even though ctx will be expired, it is good practice to call its
	// cancellation function in any case. Failure to do so may keep the
	// context and its parent alive longer than necessary.
	defer cancel()

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

}

3.WithTimeout
WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
这个例子让阻塞等待50Millisecond后结束

package main

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

func main() {
	// Pass a context with a timeout to tell a blocking function that it
	// should abandon its work after the timeout elapses.
	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()) // prints "context deadline exceeded"
	}

}

4.WithValue
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"))

}

context在并发模式中的应用

简介
在Go的服务中,每个传入请求都在自己的goroutine中进行处理。请求处理程序一般会启动另外的goroutine来访问后端,如数据库和RPC服务。这一系列在request运行的goroutine通常需要权限去访问特定的值,比如终端用户的身份,授权令牌,请求期限。当请求被取消或者超时,依赖于该request的所有goroutine应该迅速退出,以便系统可以回收他们正在使用的任何资源。

context包就是用于跨api边界的将request范围的值,取消信号,和截止时间传递给所有涉及处理request的值。该软件包可作为上下文公开使用。
关于context的类型:

// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
    // Done returns a channel that is closed when this Context is canceled
    // or times out.
    Done() <-chan struct{}

    // Err indicates why this context was canceled, after the Done channel
    // is closed.
    Err() error

    // Deadline returns the time when this Context will be canceled, if any.
    Deadline() (deadline time.Time, ok bool)

    // Value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}

上下文对于由多个goroutine同时使用是安全的。 代码可以将单个上下文传递给任意数量的goroutine,并取消该上下文以发出所有信号。
派生上下文
上下文包提供了从现有值派生新的Context值的功能。 这些值形成一棵树:取消上下文后,从该上下文派生的所有上下文也会被取消。
Background 是任何上下文树的根; 它永远不会被取消:
WithCancel和WithTimeout返回派生的Context值,该值可以比父Context早被取消。 请求处理程序返回时,通常会取消与传入请求关联的上下文。 使用多个副本时,WithCancel对于取消冗余请求也很有用。 WithTimeout对于设置对后端服务器的请求的截止日期很有用:
WithValue提供了一种将请求范围的值与Context相关联的方法

参考文章:
https://golang.org/pkg/context/
https://blog.golang.org/context

发布了222 篇原创文章 · 获赞 35 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/hello_bravo_/article/details/104483889