Повторное закрытие уже закрытого канала вызывает панику (panic):
ch := make(chan int)
close(ch)
close(ch) // panic: close of closed channel
Для всех типов каналов:
Мгновенное возникновение:
Нельзя восстановить стандартными средствами:
func main() {
ch := make(chan int)
close(ch)
close(ch) // panic
}
func main() {
ch := make(chan int)
go func() {
close(ch)
}()
close(ch) // возможна panic при одновременном закрытии
}
func main() {
ch := make(chan int, 3)
ch <- 1; ch <- 2
close(ch)
close(ch) // panic, несмотря на данные в буфере
}
Принцип единственного ответственного:
Использование sync.Once:
var once sync.Once
ch := make(chan int)
// В любой горутине:
once.Do(func() { close(ch) })
select {
case <-ch:
// канал уже закрыт
default:
close(ch) // безопасное закрытие
}
func worker(done chan struct{}) {
defer close(done) // гарантированное однократное закрытие
// работа...
}
type SafeChan struct {
ch chan int
once sync.Once
closed bool
}
func (sc *SafeChan) Close() {
sc.once.Do(func() {
close(sc.ch)
sc.closed = true
})
}
Правильное обращение с закрытием каналов - важный аспект написания надежного конкурентного кода на Go.