Что будет, если читать из закрытого канала?go-12

Поведение при чтении из закрытого канала

Когда канал закрыт, операции чтения из него имеют специфическое поведение, которое важно понимать для корректной работы программы.

Базовый случай

ch := make(chan int)
close(ch)
val := <-ch
fmt.Println(val) // Выведет: 0 (нулевое значение для int)

Ключевые особенности:

  1. Не блокирует горутину (в отличие от чтения из открытого канала)
  2. Возвращает нулевое значение типа канала
  3. Многократные чтения возможны и всегда возвращают нулевое значение

Проверка состояния канала

Используя форму с двумя возвращаемыми значениями, можно определить, было ли значение фактически получено или канал закрыт:

val, ok := <-ch
if !ok {
    fmt.Println("Канал закрыт")
}

Где:

  • ok == true: значение успешно прочитано
  • ok == false: канал закрыт и пуст

Пример с буферизированным каналом

Для буферизированного канала сначала читаются все значения из буфера, затем поведение как у небуферизированного:

ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)

fmt.Println(<-ch) // 1, ok=true
fmt.Println(<-ch) // 2, ok=true
fmt.Println(<-ch) // 0, ok=false

Использование в range

Цикл for range автоматически завершается при закрытии канала:

ch := make(chan int, 3)
ch <- 1; ch <- 2; ch <- 3
close(ch)

for v := range ch {
    fmt.Println(v) // Выведет 1, 2, 3
}
// Цикл завершится автоматически

Опасные ситуации

  1. Закрытие канала несколько раз:
close(ch)
close(ch) // panic: close of closed channel
  1. Попытка записи в закрытый канал:
close(ch)
ch <- 1 // panic: send on closed channel

Практические паттерны

1. Сигнализация о завершении

done := make(chan struct{})
go func() {
    // работа...
    close(done) // сигнал завершения
}()
<-done // ожидание завершения

2. Распределение задач с закрытием

jobs := make(chan int)
go func() {
    for j := range jobs {
        process(j)
    }
}()

// Отправка задач
for _, task := range tasks {
    jobs <- task
}
close(jobs) // сигнал что задач больше не будет

Резюмируем

  1. Чтение из закрытого канала не вызывает panic
  2. Возвращает нулевое значение типа и флаг ok == false
  3. Для буферизированных каналов сначала читаются все оставшиеся значения
  4. Цикл range автоматически останавливается при закрытии канала
  5. Закрытие канала - это идиоматический способ сигнализировать о завершении работы

Правильная работа с закрытыми каналами - важный аспект написания надежного конкурентного кода в Go.