Как читать из канала и писать в канал?go-10

Основные операции с каналами

В Go существует два основных способа взаимодействия с каналами:

  1. Запись (отправка) данных в канал
  2. Чтение (получение) данных из канала

Синтаксис операций

ch <- data  // запись данных в канал
value := <-ch  // чтение данных из канала

Запись в канал

Базовый пример записи

ch := make(chan int)
go func() {
    ch <- 42  // записываем значение в канал
}()

Особенности записи:

  1. Для небуферизированных каналов:

    • Операция записи блокирует горутину, пока другая горутина не прочитает данные
    • Обеспечивает синхронизацию между горутинами
  2. Для буферизированных каналов:

    • Запись блокируется только когда буфер заполнен
    • Позволяет горутине продолжать работу без немедленного получателя
  3. Попытка записи в закрытый канал вызывает panic:

close(ch)
ch <- 42  // panic: send on closed channel

Чтение из канала

Базовый пример чтения

value := <-ch  // чтение из канала в переменную

Варианты чтения:

  1. Простое чтение:
x := <-ch
  1. Чтение с проверкой состояния канала:
value, ok := <-ch
if !ok {
    // канал закрыт и пуст
}
  1. Чтение с использованием range:
for item := range ch {
    // обработка item
    // цикл завершится автоматически при закрытии канала
}

Особенности чтения:

  1. Чтение из небуферизированного канала блокирует горутину до появления данных
  2. Чтение из буферизированного канала:
    • Возвращает данные из буфера, если они есть
    • Блокируется только при пустом буфере
  3. Чтение из закрытого канала:
    • Возвращает нулевое значение типа
    • Второй возвращаемый параметр (ok) будет false

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

Пример 1: Простая коммуникация

func main() {
    ch := make(chan string)

    go func() {
        ch <- "Hello, World!"  // запись
    }()

    msg := <-ch  // чтение
    fmt.Println(msg)
}

Пример 2: Использование буферизированного канала

func main() {
    ch := make(chan int, 2)

    ch <- 1  // не блокируется
    ch <- 2  // не блокируется
    // ch <- 3  // заблокировалось бы (буфер полон)

    fmt.Println(<-ch)  // 1
    fmt.Println(<-ch)  // 2
}

Пример 3: Закрытие канала и range

func producer(ch chan<- int) {
    for i := 0; i < 3; i++ {
        ch <- i
    }
    close(ch)  // важно закрыть для правильной работы range
}

func consumer(ch <-chan int) {
    for n := range ch {
        fmt.Println("Received:", n)
    }
}

Распространенные ошибки

  1. Deadlock при чтении/записи:
ch := make(chan int)
<-ch  // вечная блокировка - нет писателя
// или
ch <- 42  // вечная блокировка - нет читателя
  1. Паника при записи в закрытый канал

  2. Утечка горутин из-за незакрытых каналов

Продвинутые техники

  1. Использование select для множества каналов:
select {
case msg := <-ch1:
    fmt.Println(msg)
case ch2 <- 42:
    fmt.Println("sent")
default:
    fmt.Println("no activity")
}
  1. Таймауты:
select {
case res := <-ch:
    fmt.Println(res)
case <-time.After(1 * time.Second):
    fmt.Println("timeout")
}
  1. Сигнальные каналы (использование пустой структуры):
done := make(chan struct{})
close(done)  // сигнал закрытия

Резюмируем

  1. Запись в канал: ch <- data, чтение: data := <-ch
  2. Операции блокируются в зависимости от типа канала и его состояния
  3. Всегда проверяйте состояние канала при чтении (значение ok)
  4. Используйте close() для сигнализации окончания работы с каналом
  5. Для безопасной работы с каналами используйте паттерны и идиомы Go
  6. Помните о возможных deadlock'ах и panic при неправильном использовании