老手也常误用!详解 Go channel 内存泄漏问题

本文深入探讨了 Go 语言中 channel 使用不当导致的内存泄漏问题,包括只有一方发送和多发送者的情况,并提供了增加缓冲队列和使用 stop channel 作为解决方案。通过示例代码解析,强调了正确管理 channel 关闭的重要性,以防止 goroutine 无法退出造成内存泄漏。
摘要由CSDN通过智能技术生成

一说到 go channel,很多人会使用“优秀”“哲学”这些词汇来描述。殊不知,go channel 恐怕还是 golang 中最容易造成问题的特性之一。很多情况下,我们使用 go channel 时,常常以为可以关闭 channel,但实际上却没有关闭,这就是导致 go channel 内存泄漏的元凶。

阅读本文前要求读者熟悉 go channel 的基本知识。如果你不够了解 go channel,那么可以先阅读《新手使用 go channel 需要注意的问题》。本文会默认你已经了解相关内容。

情境一:select-case 误用导致的内存泄露

废话说少,先看代码。

func TestLeakOfMemory(t *testing.T) {
   
   fmt.Println("NumGoroutine:", runtime.NumGoroutine())
   chanLeakOfMemory()
   time.Sleep(time.Second * 3) // 等待 goroutine 执行,防止过早输出结果
   fmt.Println("NumGoroutine:", runtime.NumGoroutine())
}

func chanLeakOfMemory() {
   
   errCh := make(chan error) // (1)
   go func() {
    // (5)
      time.Sleep(2 * time.Second)
      errCh <- errors.New("chan error") // (2)
      fmt.Println("finish sending")
   }()

   var err error
   select {
   
   case <-time.After(time.Second): // (3) 大家也经常在这里使用 <-ctx.Done()
      fmt.Println("超时")
   case err = <-errCh: // (4)
      if err != nil {
   
         fmt.Println(err)
      } else {
   
         fmt.Println(nil)
      }
   }
}

执行代码(需注意,使用测试和 main 线程执行的输出略有不同)

大家认为输出的结果是什么?正确的输出结果如下:

NumGoroutine: 2
超时
NumGoroutine: 3

这是 go channel 导致内存泄漏的经典场景。根据输出结果(开始有两个 goroutine,结束时有三个 goroutine),我们可以知道,直到测试函数结束前,仍有一个 goroutine 没有退出。原因是由于 (1) 处创建的 errCh 是不含缓存队列的 channel,如果 channel 只有发送方发送,那么发送方会阻塞;如果 channel 只有接收方

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值