package main

import (
   "context"
   "fmt"
   "time"
)

func Task ()  {
   ctx,_:=context.WithTimeout(context.Background(),time.Second*1)
   check :=make(chan struct{})
   go func() {
      time.Sleep(time.Millisecond*1200)
      check<- struct{}{}
   }()
   select {
   case <-check:
      fmt.Println("未超时")
   case <-ctx.Done():
      fmt.Println("超时了")
   }
}

func main()  {
   for i:=0;i<=20;i++ {
      go Task()
   }
   for  {
   }
}
  

此程序会造成什么后果?

在main方法中添加如下代码

 func main()  {
   for i:=0;i<=20;i++ {
      go Task()
   }
   for  {
      time.Sleep(time.Second*2)
      fmt.Println(runtime.NumGoroutine())
   }
}
   

输出结果

可以看到运行中协程数量一直维持在22造成内存泄露。

原因是定义管道check的时候是无缓存的所以在程序超时时check无法处理,外层task结束而里面go协程继续等待,协程无法释放一直占用内存导致内存泄露。修改方法如下:

 func Task ()  {
   ctx,_:=context.WithTimeout(context.Background(),time.Second*1)
   //增加一个缓存区
   check :=make(chan struct{},1)
   go func() {
      time.Sleep(time.Millisecond*1200)
      check<- struct{}{}
   }()
   select {
   case <-check:
      fmt.Println("未超时")
   case <-ctx.Done():
      fmt.Println("超时了")
   }
}  

无缓冲channel和有缓存channel区别:

c1:=make(chan int) 无缓冲

c2:=make(chan int,1) 有缓冲

c1<-1

无缓冲的 不仅仅是 向 c1 通道放 1 而是 一直要有别的携程 <-c1 接手了 这个参数,那么c1<-1才会继续下去,要不然就一直阻塞着

而 c2<-1 则不会阻塞,因为缓冲大小是1 (其实是缓冲大小为0)只有当 放第二个值的时候 第一个还没被人拿走,这时候才会阻塞。