Golang基础学习——context包的使用

134 阅读2分钟

context的基础接口结构:

type Context interface {
   Deadline() (deadline time.Time, ok bool)
   Done() <-chan struct{}
   Err() error
   Value(key any) any
}

作用:控制子协程的退出(内部其实还是通过channel的通信来实现,对其进行了封装)

  • Deadline() (deadline time.Time, ok bool)
    • 返回工作的截止时间. (若没有, 则 ok=false)
  • Done() <-chan struct{}
    • 返回一个管道. 若从管道中可以读到数据, 说明任务被关闭了. (若不需要, 返回 nil)
    • 通常做法为在需要退出时将管道关闭.
  • Err() error
    • 返回任务被关闭的原因.
  • Value(key any) any
    • 返回上下文包含的环境变量

WithCancel

从父context中复制出一个带有关闭函数的context,一旦其被调用或者父进程结束,这个子进程就结束了,以下为一个例子,此例子中子进程为一个定时任务,每隔一秒从msg channel中读取接收的信息,主进程将0-4这5个数传入msg channel中

func main() {
   ctx, close := context.WithCancel(context.Background())
   message := make(chan int)
   go son(ctx,message)
   for i := 0; i < 5; i++ {
      message <- i
   }
   close()
   time.Sleep(2 * time.Second)
   fmt.Println("主进程结束")
}

func son(ctx context.Context,msg chan int) {
   t := time.Tick(time.Second)
   for range t {
      select {
      case m := <- msg:
         fmt.Printf("接收到信息:%d \n",m)

      case <- ctx.Done():
         fmt.Println("子协程结束")
         return
      }
   }
}

WithDeadline

可以设置一个过期的时间,到时间后context关闭

func main() {
   ctx, close := context.WithDeadline(context.Background(), time.Now().Add(time.Second * 2))
   message := make(chan int)
   go son(ctx,message)
   for i := 0; i < 5; i++ {
      message <- i
   }
   defer close()
   //time.Sleep(time.Second)
   fmt.Println("主进程结束")
}

func son(ctx context.Context,msg chan int) {
   t := time.Tick(time.Second)
   for range t {
      select {
      case m := <- msg:
         fmt.Printf("接收到信息:%d \n",m)

      case <- ctx.Done():
         fmt.Println("子协程结束")
         return
      }
   }
}

如图所示在2秒后子协程关闭,由于msg channel中有数据,主线程处于阻塞状态
image.png
红圈表示主线程并没有结束,处于阻塞的状态

WithTimeout

可以设置超时时长,超过时长自动关闭,与WithDeadline类似,一个设置超时时长,一个设置特定时间关闭的context

WithValue

在复制出来的新的context中可以携带一个k - v的环境变量,如下例子所示(在withCancel上改进了一下):在此ctx中就可以带有name - panda这一键值对的一个环境变量

func main() {
   ctxValue := context.WithValue(context.Background(), "name", "panda")
   ctx, close := context.WithCancel(ctxValue)
   message := make(chan int)
   go son(ctx,message)
   for i := 0; i < 5; i++ {
      message <- i
   }
   close()
   time.Sleep(2 * time.Second)
   fmt.Println("主进程结束")
}

func son(ctx context.Context,msg chan int) {
   t := time.Tick(time.Second)
   for range t {
      select {
      case m := <- msg:
         fmt.Printf("接收到信息:%d \n",m)

      case <- ctx.Done():
         fmt.Println("子协程结束",ctx.Value("name"))
         return
      }
   }
}

本文正在参加技术专题18期-聊聊Go语言框架