Когда канал закрыт, операции чтения из него имеют специфическое поведение, которое важно понимать для корректной работы программы.
ch := make(chan int)
close(ch)
val := <-ch
fmt.Println(val) // Выведет: 0 (нулевое значение для int)
Используя форму с двумя возвращаемыми значениями, можно определить, было ли значение фактически получено или канал закрыт:
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
Цикл 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
}
// Цикл завершится автоматически
close(ch)
close(ch) // panic: close of closed channel
close(ch)
ch <- 1 // panic: send on closed channel
done := make(chan struct{})
go func() {
// работа...
close(done) // сигнал завершения
}()
<-done // ожидание завершения
jobs := make(chan int)
go func() {
for j := range jobs {
process(j)
}
}()
// Отправка задач
for _, task := range tasks {
jobs <- task
}
close(jobs) // сигнал что задач больше не будет
ok == false
range
автоматически останавливается при закрытии каналаПравильная работа с закрытыми каналами - важный аспект написания надежного конкурентного кода в Go.