- 有关channel的问题
- 保证channel关闭一次的方式
- 关闭channel的优雅方式
一、有关channel的问题
Golang面试有关channel的问题肯可能会问到:
Q:如何关闭一个channel
A:close()
Q:怎么判断一个channel已经关闭了
A:(1) value, ok := <- ch // ok为false,则通道已经关闭; (2) <-ch // 如果channel关闭,这里就不会阻塞,而这句代码可以用在select中或直接在执行代码中作为阻塞机制。
而在实际工作中,我也曾遇到过一个项目的Bug,根因就是关闭了已经关闭的channel,而出现了panic。
于是应该考虑使用一种机制,当多个goroutine使用一个channel进行发送或接收工作的时候,保证在goroutine中只执行一次关闭channel的操作。
二、保证channel关闭一次的方式
当以多个goroutine的方式执行下面的两个函数,显然在第一个SafeClose()的goroutine执行便会关闭通道ch,而第二个SafeClose()的goroutine执行便会出现panic。于是这里通过recover()的方式来避免panic。
简单来说,就是忽视关闭已经关闭的channel而产生的panic。显然,这不是一种好的方法。
func SafeSend(ch chan int, value int) (closed bool) {
defer func() {
if recover() != nil {
closed = true
}
}()
ch <- value
return false
}
func SafeClose(ch chan int) (justClosed bool) {
defer func() {
if recover() != nil {
justClosed = false
}
}()
close(ch)
return true
}
下面的方法是通过使用到sync.Once,来保证关闭channel的操作只执行一次。使用的方式没什么毛病,在诸多Golang开源项目中,大多使用sync.Once来关闭channel。
type MyChannel struct {
C chan int
once sync.Once
}
func NewMyChannel() *MyChannel {
return &MyChannel{
C: make(chan int)}
}
func (mc *MyChannel) SafeClose() {
mc.once.Do(func(){
close(mc.C)
})
}
下面的例子是通过加锁和设置关闭标志,在锁中执行通道关闭和对关闭标志赋值,从而保证channel在初始关闭标志状态下只被关闭一次。
type MyChannel1 struct {
C chan int
closed bool
mutex sync.Mutex
}
func NewMyChannel1() *MyChannel