zoukankan      html  css  js  c++  java
  • go Context的使用

    控制并发有两种经典的方式,一种是WaitGroup,另外一种就是Context

    WaitGroup的使用

    • WaitGroup可以用来控制多个goroutine同时完成
    	func main() {
    	var wg sync.WaitGroup
    	wg.Add(2)
    	go func() {
    		time.Sleep(2*time.Second)
    		fmt.Println("1号完成")
    		wg.Done()
    	}()
    	go func() {
    		time.Sleep(2*time.Second)
    		fmt.Println("2号完成")
    		wg.Done()
    	}()
    	wg.Wait()
    	fmt.Println("好了,大家都干完了,放工")
    }
    
    以上例子一定要等到两个goroutine同时做完才会全部完成,这种控制并发方式尤其适用于多个goroutine协同做一件事情的时候。
    

    chan通知

    • chan也可以用于控制goroutine,通过chan来控制goroutine是否结束
    	func main() {
    	stop := make(chan bool)
    
    	go func() {
    		for {
    			select {
    			case <-stop:
    				fmt.Println("监控退出,停止了...")
    				return
    			default:
    				fmt.Println("goroutine监控中...")
    				time.Sleep(2 * time.Second)
    			}
    		}
    	}()
    
    	time.Sleep(10 * time.Second)
    	fmt.Println("可以了,通知监控停止")
    	stop<- true
    	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
    	time.Sleep(5 * time.Second)
    }
    
    例子中我们通过select判断stop是否接受到值,如果接受到值就表示可以推出停止了,如果没有接受到,就会执行default里面的监控逻辑,继续监控,直到收到stop的通知
    
    以上控制goroutine的方式在大多数情况下可以满足我们的使用,但是也存在很多局限性,比如有很多goroutiine,并且这些goroutine还衍生了其他goroutine,此时chan就比较困难解决这样的问题了
    

    Context

    以上问题是存在的,比如一个网络请求request,每个request都需要开启一个goroutine做一些事情。所以我们需要一种可以跟踪goroutine的方案才可以达到控制的目的,go为我们提供了Context

    func main() {
    	ctx, cancel := context.WithCancel(context.Background())
    	go watch(ctx,"【监控1】")
    	go watch(ctx,"【监控2】")
    	go watch(ctx,"【监控3】")
    	time.Sleep(10 * time.Second)
    	fmt.Println("可以了,通知监控停止")
    	cancel()
    	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
    	time.Sleep(5 * time.Second)
    }
    func watch(ctx context.Context, name string) {
    	for {
    		select {
    		case <-ctx.Done():
    			fmt.Println(name,"监控退出,停止了...")
    			return
    		default:
    			fmt.Println(name,"goroutine监控中...")
    			time.Sleep(2 * time.Second)
    		}
    	}
    }
    

    例子中启动了3个监控goroutine进行不断的监控,每一个都使用Context进行跟踪,当我们使用cancel函数通知取消时候,这3个 goroutine都会被结束。所有基于这个context或者衍生出来的子Context都会收到通知,这样就可以进行清理操作最终释放goroutine了

    Context接口

    Context是一个接口,具体的内容如下:

    type Context interface {
    	Deadline() (deadline time.Time, ok bool)
    	Done() <-chan struct{}
    	Err() error
    	Value(key interface{}) interface{}
    }
    
    • Deadline方法是获取设置的截止时间的意思,第一个返回式是截止时间,到了这个时间点,Context会自动发起取消请求;第二个返回值ok==false时表示没有设置截止时间,如果需要取消的话,需要调用取消函数进行取消
    • Done方法返回一个只读的chan,类型为struct{},我们在goroutine中,如果该方法返回的chan可以读取,则意味着parent context已经发起了取消请求,我们通过Done方法收到这个信号后,就应该做清理操作,然后退出goroutine,释放资源
    • Err方法返回取消的错误原因,因为什么Context被取消。
    • Value方法获取该Context上绑定的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的

    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
    

    这四个With函数,接收的都有一个partent参数,就是父Context,我们要基于这个父Context创建出子Context的意思

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

    Context使用原则

    1. 不要把Context放在结构体中,要以参数的方式进行传递
    2. 以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位
    3. 给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用context.TODO
    4. Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递
  • 相关阅读:
    MySql安装方法和配置、解决中文乱码
    医疗器械软件安全性级别判定
    工业镜头视场、倍率、焦距之间的关系
    C#一个进程监控另一进程的用户界面是否响应系统
    TextEdit验证
    "tsc.exe"已退出,代码1
    截取上传视频的第一帧作为封面的方法
    汉堡也会结对子
    第二次作业——全国中小学生数学(四则运算)竞赛
    第二次作业——全国中小学生数学(四则运算)竞赛(更改版)
  • 原文地址:https://www.cnblogs.com/develop-SZT/p/10398083.html
Copyright © 2011-2022 走看看