其他分享
首页 > 其他分享> > Contex包了解一下

Contex包了解一下

作者:互联网

Context包了解一下

为啥需要Context

​ 在并发编程中,由于超时、取消操作或者一些异常情况,往往需要进行抢占操作或者中断后续操作,context营运而生,channel也能用但是层级太深了的话channel就不好用了。

Context接口

type Context interface {

    Deadline() (deadline time.Time, ok bool)

    Done() <-chan struct{}

    Err() error

    Value(key interface{}) interface{}
}

Context接口有四个函数

创建Context

创建空的Context

ctx := context.Background()
ctx := context.TODO()

这两个函数都返回一个空的context,他们不能被取消,没有deadline,没有key-value.

派生Context

空context肯定不能满足我们的需求,我们需要根据需求获得好用的context

下面演示如果获取一个可以被取消的context

ctx ,cancel := context.WithCancel(ctx)  //ctx是一个空的context

contex.WithCancel函数接收一个context作为父context,派生出一个可以cancel的子context,然后返回子context和一个函数,调用这个函数就会取消context。

下面两个函数也用来派生context

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) //返回带有timeout的context,到时间自动cancel或者你手动cancel
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)  //返回带有deadline的context,需要设置一个具体时间。
func WithValue(parent Context, key, val interface{}) Context  //返回带有key-value的context

举例

在本地,函数之间

这个例子是这样的,我么用一个函数去做一件事,他要一些时间去执行(这里定位5s),然后我们起一个协程,2s后执行cancel(),就是将context关掉,限制任务执行时间。我们通过select监听context.Done()去看有没有被cancel,用time.After去完成5s这个计时。

当2s到的时候,cancel执行,然后ctx.Done()就有一个关闭的通道返回了

func doSomething(ctx context.Context) {
	select {
	case <-time.After(5 * time.Second): //5秒后开始干活
		fmt.Println("finish do something")
	case <-ctx.Done(): //监听context有没有被cancel,被cancel后ctx.Done()就会返回一个通道
		err := ctx.Err()
		if err != nil {
			fmt.Println(err.Error())
		}
	}
}

func main() {
	ctx := context.Background() //返回一个空的context
	ctx, cancel := context.WithCancel(ctx)

	go func() { //2秒后将context给cancel掉
		time.Sleep(2 * time.Second)
		cancel()
	}()
    doSomething(ctx)
}

最后运行的效果就是:

image-20220625155628735

客户端与服务器之间

我们写一个server,接收http请求,然后再写一个client,client向server发送请求,带一个context过去,这个context有一个timeout

client:向server发送请求,带一个timeout为3秒的context过去。

func main(){

	ctx := context.Background()
	ctx ,cancel := context.WithTimeout(ctx,3 * time.Second)
	defer cancel()

	rep,err := http.NewRequest(http.MethodGet,"http://localhost:8080",nil)
	if err != nil {
		log.Fatalln(err.Error())
	}
	rep = rep.WithContext(ctx)
	
	resp,err := http.DefaultClient.Do(rep)
	if err != nil {
		log.Fatal(err.Error())
	}
	defer resp.Body.Close()

	respBytes,err := io.ReadAll(resp.Body)

	if err != nil {
		log.Panicln(err.Error())
	}
	fmt.Println(string(respBytes))
}

server:server接收请求中的context进行处理。server回5秒再返回helloworld但是timeout设置的是3秒,所以server这里会收到ctx.Done()的信号,然后就会打印一些东西。

func handler(w http.ResponseWriter,r *http.Request) {
	
	ctx := r.Context()
	select {
	case <-time.After(5*time.Second):
		_,err := fmt.Fprintln(w,"hello world")
		if err != nil {
			log.Panicln(err.Error())
		}
	case <-ctx.Done():
		err := ctx.Err()
		if err != nil {
			fmt.Println(err.Error())
		}
	}
	log.Println("handler done")
}

func main() {
	http.HandleFunc("/",handler)
	http.ListenAndServe(":8080",nil)
}

client执行效果:

image-20220625184640367

server执行效果:

image-20220625184656663

这样的话我们可以在超时之后return掉,不浪费资源。

标签:返回,context,err,一下,ctx,Contex,了解,Context,cancel
来源: https://www.cnblogs.com/yumingkuan/p/16412217.html