ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Contex包了解一下

2022-06-25 19:00:06  阅读:193  来源: 互联网

标签:返回 context err 一下 ctx Contex 了解 Context cancel


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接口有四个函数

  • Deadline: 返回绑定当前context的任务被取消的时间,如果没有设置时间,就返回ok == false
  • Done:在context的任务被取消时,返回一个关闭的channel,如果contex不能被取消,就返回nil
  • Err:如果Done返回的channel没有被关闭,返回nil,如果关闭了,就返回错误表示任务结束的原因,如果是context被取消,就返回Canneled,如果是超时,返回DeadLineExceeded
  • Value:返回context存储的键值对中的key对应的值,如果没有就返回nil。

创建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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有