← Назад к вопросам
Что выведет код? Закрытый канал
2.0 Middle🔥 161 комментариев
#Основы Go
Условие
Определите, что выведет следующий код:
package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for i := 0; i < 5; i++ {
val, ok := <-ch
fmt.Printf("val=%d, ok=%t\n", val, ok)
}
}
Вопросы
- Что выведет программа?
- Что возвращается при чтении из закрытого канала?
- Что произойдёт при попытке записи в закрытый канал?
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Что выведет код с закрытым каналом
Ответ на вопрос 1
Программа выведет:
val=1, ok=true
val=2, ok=true
val=3, ok=true
val=0, ok=false
val=0, ok=false
Почему такой результат?
Шаг 1: Создание и заполнение канала
ch := make(chan int, 3) // Буферизированный канал на 3 элемента
ch <- 1 // Элемент 1 в буфер
ch <- 2 // Элемент 2 в буфер
ch <- 3 // Элемент 3 в буфер
// Буфер полон: [1, 2, 3]
Шаг 2: Закрытие канала
close(ch) // Канал закрыт, но элементы остаются в буфере
Шаг 3: Первые три итерации цикла
i=0: val, ok := <-ch // Читаем 1 из буфера
ok=true (элемент найден)
fmt.Printf("val=1, ok=true\n")
i=1: val, ok := <-ch // Читаем 2 из буфера
ok=true (элемент найден)
fmt.Printf("val=2, ok=true\n")
i=2: val, ok := <-ch // Читаем 3 из буфера
ok=true (элемент найден)
fmt.Printf("val=3, ok=true\n")
Шаг 4: Две последние итерации (буфер пуст, канал закрыт)
i=3: val, ok := <-ch // Буфер пуст, канал закрыт
val=0 (нулевое значение для int)
ok=false (канал закрыт)
fmt.Printf("val=0, ok=false\n")
i=4: val, ok := <-ch // Буфер пуст, канал закрыт
val=0 (нулевое значение для int)
ok=false (канал закрыт)
fmt.Printf("val=0, ok=false\n")
Ответ на вопрос 2: Что возвращается при чтении из закрытого канала?
При чтении из закрытого канала возвращаются два значения:
val, ok := <-ch
// val = нулевое значение типа канала (0 для int, "" для string, nil для pointer)
// ok = false (сигнализирует, что канал закрыт)
Для разных типов:
// Закрытый канал int
val, ok := <-ch // val=0, ok=false
// Закрытый канал string
val, ok := <-ch // val="", ok=false
// Закрытый канал структуры
val, ok := <-ch // val=MyStruct{}, ok=false (нулевое значение структуры)
// Закрытый канал указателя
val, ok := <-ch // val=nil, ok=false
Проверка закрытости канала:
if val, ok := <-ch; !ok {
// Канал закрыт
fmt.Println("Channel closed")
} else {
// Канал открыт и есть значение
fmt.Printf("Got value: %d\n", val)
}
Ответ на вопрос 3: Что произойдёт при записи в закрытый канал?
Программа ПАНИКУЕТ!
ch := make(chan int, 1)
close(ch)
ch <- 1 // PANIC: send on closed channel
Runtime panic:
panic: send on closed channel
goroutine 1 [running]:
main.main()
main.go:10 +0x40
Таблица операций с закрытым каналом
| Операция | Поведение | Результат |
|---|---|---|
| Чтение из закрытого | Успешно (если есть буфер) | val=default, ok=false |
| Запись в закрытый | PANIC | panic: send on closed channel |
| Закрытие закрытого | PANIC | panic: close of closed channel |
| len() закрытого | Работает | Возвращает кол-во элементов в буфере |
| cap() закрытого | Работает | Возвращает размер буфера |
Примеры для каждого случая
Пример 1: Чтение буфера после close
func main() {
ch := make(chan int, 2)
ch <- 10
ch <- 20
close(ch)
// Чтение существующих элементов
fmt.Println(<-ch) // 10
fmt.Println(<-ch) // 20
fmt.Println(<-ch) // 0 (нулевое значение)
// Проверка состояния
if val, ok := <-ch; !ok {
fmt.Println("Channel is closed") // Выведется
}
}
Пример 2: Запись в закрытый канал (PANIC)
func main() {
ch := make(chan int, 1)
close(ch)
ch <- 1 // PANIC!
}
Пример 3: Итерация по канальу (безопасно)
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
// for range автоматически завершится при close
for val := range ch {
fmt.Println(val)
}
// Выведет: 1, 2, 3
}
Пример 4: Проверка закрытости без паники
func isClosed(ch chan int) bool {
// Небезопасный способ
select {
case <-ch:
return true
default:
return false
}
}
// ЛУЧШЕ: использовать флаг
type SafeChannel struct {
ch chan int
closed bool
mu sync.Mutex
}
func (sc *SafeChannel) Send(val int) error {
sc.mu.Lock()
defer sc.mu.Unlock()
if sc.closed {
return fmt.Errorf("channel is closed")
}
sc.ch <- val
return nil
}
func (sc *SafeChannel) Close() {
sc.mu.Lock()
defer sc.mu.Unlock()
if !sc.closed {
close(sc.ch)
sc.closed = true
}
}
Важные правила
- Закрытый канал читается без проблем, пока есть элементы в буфере
- При попытке чтения из пустого закрытого канала возвращается нулевое значение и ok=false
- Запись в закрытый канал ВСЕГДА паникует
- Закрытие закрытого канала паникует
- For range автоматически завершается при close(ch)
Паттерн для безопасной работы
// Правило: только отправитель может закрывать канал
func main() {
ch := make(chan int)
go func() {
// Отправитель
for i := 0; i < 5; i++ {
ch <- i
}
close(ch) // Только отправитель!
}()
// Получатель
for val := range ch {
fmt.Println(val)
}
}
Это важная особенность Go — правильная работа с каналами критична для избежания паник в production коде.