← Назад к вопросам

Что выведет код? Закрытый канал

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. Что выведет программа?
  2. Что возвращается при чтении из закрытого канала?
  3. Что произойдёт при попытке записи в закрытый канал?

Комментарии (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
Запись в закрытыйPANICpanic: send on closed channel
Закрытие закрытогоPANICpanic: 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
    }
}

Важные правила

  1. Закрытый канал читается без проблем, пока есть элементы в буфере
  2. При попытке чтения из пустого закрытого канала возвращается нулевое значение и ok=false
  3. Запись в закрытый канал ВСЕГДА паникует
  4. Закрытие закрытого канала паникует
  5. 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 коде.

Что выведет код? Закрытый канал | PrepBro