通道使用后需要关闭
通道中的数据是占用资源的,使用后及时关闭通道,可以保护数据安全,防止资源泄露;以及可以提高程序的性能,避免资源浪费
未关闭通道的风险
1、通道中的资源可能会泄露
2、持续占用系统资源,导致性能下降
3、遍历一个不关闭的channel会造成死循环
如何优雅的关闭通道?
-
使用Go通道的一个基本原则:不要在接收方关闭通道,确保关闭通道的是唯一发送方
-
不要关闭(或者发送值到)已经被关闭了的通道
简单粗暴的关闭方案:defer close(chanName)
礼貌一点:使用``sync.Once`
type MyChannel struct {
C chan T
once sync.Once
}
func NewMyChannel() *MyChannel {
return &MyChannel{C: make(chan T)}
}
func (mc *MyChannel) SafeClose() {
mc.once.Do(func() {
close(mc.C)
})
}
也可以使用``sync.Mutex`避免多次关闭同一通道
type MyChannel struct {
C chan T
closed bool
mutex sync.Mutex
}
func NewMyChannel() *MyChannel {
return &MyChannel{C: make(chan T)}
}
func (mc *MyChannel) SafeClose() {
mc.mutex.Lock()
defer mc.mutex.Unlock()
if !mc.closed {
close(mc.C)
mc.closed = true
}
}
func (mc *MyChannel) IsClosed() bool {
mc.mutex.Lock()
defer mc.mutex.Unlock()
return mc.closed
}
优雅的关闭方案:使用sync.WaitGroup
情况1:单生产者,单消费者
- 直接让生产者关闭channel即可
情况2:单生产者,多消费者
- 同样直接让生产者关闭channel即可
情况3:多生产者,单消费者
- 让接收者关闭一个额外的通知通道来告知发送者停止发送
情况4:多生产者,多消费者
- 引入一个信号通道,用于通知所有的生产者和消费者停止工作
有缓存和无缓存通道的区别
无缓冲
无缓冲通道在赋值后不读取就会进入阻塞
发送和接收的交互行为是同步的,且无法单独存在
有缓冲
有缓冲通道赋值后,值会进入缓冲区,在缓冲区满后阻塞