Ответ
Основное правило в Go: закрывать канал должна та горутина, которая в него пишет (отправитель). Никогда не закрывайте канал со стороны читающей горутины.
Почему именно отправитель?
-
Предотвращение паники: Попытка отправить значение в уже закрытый канал вызывает панику (
panic: send on closed channel
). Отправитель точно знает, когда он закончил отправлять данные, и может безопасно закрыть канал. Читатель же никогда не знает, не собирается ли отправитель послать что-то еще. -
Сигнал о завершении: Закрытие канала — это сигнал для всех читающих горутин о том, что данных больше не будет. Это позволяет читателям корректно завершить свою работу.
Как читатель узнает, что канал закрыт?
Читающая горутина может использовать две формы для проверки состояния канала:
-
Цикл
for range
: Этот цикл автоматически прекращается, когда канал закрывается и в нем не остается данных.for value := range ch { // Обработка value } // Цикл завершится, когда ch будет закрыт
-
Проверка второго значения: При чтении из канала можно получить второй параметр
ok
(типаbool
). Еслиok
равноfalse
, это значит, что канал закрыт и данных в нем больше нет.value, ok := <-ch if !ok { // Канал закрыт, выходим }
Что делать, если писателей несколько?
Если в один канал пишут несколько горутин, закрывать его должен тот, кто координирует их работу. Обычно для этого используют sync.WaitGroup
, чтобы дождаться завершения всех писателей, и только после этого закрыть канал в отдельной горутине.
func main() {
var wg sync.WaitGroup
ch := make(chan int)
// Запускаем несколько писателей
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
ch <- id * 10
}(i)
}
// Горутина, которая ждет завершения всех писателей и закрывает канал
go func() {
wg.Wait()
close(ch)
}()
// Читатель
for val := range ch {
fmt.Println(val)
}
}