go协程控制——context

context

为什么有context

  1. 首先,如果我们在并发程序中,如果需要我们去通知子协程结束我们会怎么做?

  2. 我们可能会通过一个channel+select去通知,如下:

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main()  {
         
    	exitChan := make(chan bool)
    
    	go func() {
         
    		for {
         
    			fmt.Println("Doing Work......")
    			select {
         
    				case <-exitChan:
    					return
    			}
    		}
    	}()
    
    	time.Sleep(time.Second * 1)
    	fmt.Println("stop the goRoutinue")
    	close(exitChan)
    	time.Sleep(time.Second * 3)
    }
    
  3. 这种做法能传递我们需要给子协程的信号,但是他是有限的,比如如果我想在指定的时间间隔内通知,想传递为什么取消的信息,如果需要控制子协程以及子协程的子协程,多个层级下使用exit Channel的方式会变得混乱复杂,所以官方就为goroutine控制开发了context包

什么是context

  1. context是协程并发安全的

  2. context包可以从已有的context实例派生新的context,形成context的树状结构,只要一个context取消了,派生出来的context将都会被取消

创建context树:

  1. 第一步创建根结点, 使用emptyCtx(int类型变量)…context.Background经常用做context树的根结点,由接收请求的第一个routine创建,不能被取消,没有值也没有过期时间

    type emptyCtx int
    
    func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
         
    	return
    }
    
    func (*emptyCtx) Done() <-chan struct{
         } {
         
    	return nil
    }
    
    func (*emptyCtx) Err() error {
         
    	return nil
    }
    
    func (*emptyCtx) Value(key interface{
         }) interface{
         } {
         
    	return nil
    }
    // emptyCtx不能存储额外信息,没有超时时间,不能取消,都是nil
    
    // Background returns a non-nil, empty Context. It is never canceled, has no
    // values, and has no deadline. It is typically used by the main function,
    // initialization, and tests, and as the top-level Context for incoming
    // requests.
    func Background() Context {
         
     return background
    }
    
    // TODO returns a non-nil, empty Context. Code should use context.TODO when
    // it's unclear which Context to use or it is not yet available (because the
    // surrounding function has not yet been extended to accept a Context
    // parameter).
    func TODO() Context {
         
     return todo
    }
    

    通过注释,可以看到context.Background返回一个非空的context,通常由主函数,初始化和测试使用,并作为传入请求的top-level Context (顶级上下文)。TODO也是返回一个非空的context,当不知道应该使用context时使用TODO…两者只是使用场景不同,代码实现都是一样

  2. 创建子孙节点由四个函数

    • func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
      // 对应cancelCtx(参考文末的structure图)
      // 返回ctx和cancel函数,可以让后代的goroutine退出,关闭对应的c.done
      // 创建了可取消的cancelCtx类型 =》 对应下面的cancelCtx
      func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
         
      	c := newCancelCtx(parent)
      	propagateCancel(parent, &c)
      	return &c, func() {
          c.cancel(true, Canceled) }
      }
    
    // newCancelCtx returns an initialized cancelCtx.,生成新的子节点
    func newCancelCtx(parent Context) cancelCtx {
         
    	return cancelCtx{
         Context: parent}
    }
    
    // propagateCancel arranges
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值