【GoLang】关于通道Channel 的关闭

在go中,channel的使用频率很高,那么你知道关于channel关闭的知识吗?

为什么要关闭?不关闭会有什么风险?

在某些情况下,不关闭 channel 就会造成内存泄漏。

例如:channel 的发送次数小于接收次数时,接收者 go routine 由于等待发送者发送一直阻塞。因此接收者 go routine 一直未退出,channel内的数据也由于一直被接收者使用无法被垃圾回收。未退出的 go routine 和未被回收的 channel 都造成了内存泄漏的问题。

当 channel 的发送次数大于接收次数时,也会出现同样的问题导致内存泄漏。

补充关于内存回收的两点:

  1. 当一个 Goroutine 退出时,它所使用的内存资源会被回收。如果 Goroutine 中创建了其他资源(如 Channel、互斥锁等),这些资源也会被一并回收。
  2. 当一个 Channel 被关闭后,如果没有任何 Goroutine 持有该 Channel 的引用,Channel 占用的内存资源会被回收。

另外,并不是所有情况都需要关闭channel:
当 channel 的发送和接收次数确定且相同时,发送者 go routine 和接收者 go routine 分别都会在发送或接收结束时结束各自的 go routine。channel中的数据也会由于没有代码使用被垃圾收集器回收。因此这种情况下,不关闭 channel,没有任何副作用。

总结下来,当 channel 的发送和接收次数确定且相同时,不需要关闭。channel 的发送次数不确定时,需要关闭。

如何优雅地关闭

首先要遵循以下两点规律:

  1. 应该只在发送端关闭 channel。(防止关闭后继续发送)
  2. 存在多个发送者时不要关闭发送者 channel,而是使用专门的 stop channel。(因为多个发送者都在发送,且不可能同时关闭多个发送者,否则会造成重复关闭。发送者和接收者多对一时,接收者关闭 stop channel;多对多时,由任意一方关闭 stop channel,双方监听 stop channel 终止后及时停止发送和接收)

另外,golang 官方为我们提供了一种方式,可以用来尽量避免这个问题。golang 允许我们在把 channel 作为参数时,使用 <- 控制 channel 在函数内只读或只写,防止我们在错误的时候关闭 channel。

 func TestOneSenderOneReceiver(t *testing.T) {
    ich := make(chan int)
    go sender(ich)
    go receiver(ich)
 }
 
 // 发送者​
 func sender(ich chan<- int) { // 注意参数中的箭头
    for i := 0; i < 100; i++ {
       ich <- i
    }
 }
 
 // 接收者​
 func receiver(ich <-chan int) {  // 注意参数中的箭头
    fmt.Println(<-ich)
    close(ich) // 此处代码会在编译期报错
 }

有缓存通道与无缓存通道的区别

创建方式

无缓冲通道

unbufferChan := make(chan int)

有缓存通道

bufferChan := make(chan int, 2)	//创建了一个缓存区大小为2的有缓存通道

阻塞机制

  • 无缓冲通道
    当一个 Goroutine 向无缓存通道发送数据时,该 Goroutine 会被阻塞,直到另一个 Goroutine 从该通道接收数据。
    同理,当一个 Goroutine 试图从无缓存通道接收数据时,该 Goroutine 也会被阻塞,直到另一个 Goroutine 向该通道发送数据。

  • 有缓存通道
    有缓存通道会在内部维护一个缓存区,用于存储发送到通道但还未被接收的数据。
    当向有缓存通道发送数据时, 如果缓存区还有空间, 数据会被发送到缓存区中等待被接收, 否则发送者 Gorutine 会被阻塞, 直到有空间可用。
    当从一个有缓存通道接收数据时, 如果缓存区有数据, 会立刻接收到该数据, 否则接收者 Groutine 会被阻塞, 直到有数据可接收。

同步与异步

  • 无缓冲通道
    无缓存通道在发送和接收操作上是"同步"的,也就是说,发送者必须等待接收者接收数据,接收者也必须等待发送者发送数据。

  • 有缓存通道
    有缓存通道在发送和接收操作上是"异步"的,也就是说,发送者可以先将数据发送到通道缓存区,而不需要等待接收者接收数据。

总的来说,无缓冲通道更加简单和直观,但需要更加小心地管理 Goroutine 的生命周期;而有缓存通道提供了更大的灵活性,但也增加了一定的复杂性。根据具体的需求选择合适的通道类型是非常重要的。

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值