Перейти к основному содержимому

Типы каналов

1. Небуферизированные каналы (Unbuffered Channels)

Это каналы без памяти. По умолчанию, если вы создаете канал через make(chan int), он является небуферизированным.

  • Как это работает: Отправитель и получатель должны встретиться в одной точке («рандеву»). Отправитель блокируется до тех пор, пока получатель не будет готов принять данные. Получатель блокируется до тех пор, пока отправитель не начнет передачу.
  • Синхронизация: Это не просто передача данных, это полная синхронизация. Вы точно знаете, что данные были успешно получены в момент отправки.

Пример:

ch := make(chan string) // Небуферизированный канал

go func() {
fmt.Println("Горутина: готова отправить...")
ch <- "Привет!" // Зависает здесь, пока main не дойдет до чтения
fmt.Println("Горутина: отправила!")
}()

time.Sleep(time.Second) // Имитация задержки в main
fmt.Println("Main: читаю из канала...")
msg := <-ch
fmt.Println("Main: получил", msg)

Здесь горутина будет "висеть" на строке ch <- "Привет!", пока main не дойдет до строки <-ch.


2. Буферизированные каналы (Buffered Channels)

Это каналы с «памятью» (буфером) определенного размера. Мы создаем их, указывая емкость: make(chan int, 3).

  • Как это работает: Отправитель может отправить данные в канал и сразу идти дальше, если в буфере есть свободное место. Получатель может забрать данные из буфера, даже если отправитель уже давно занят другими делами.
  • Синхронизация: Блокировка происходит только если буфер полон (для отправителя) или пуст (для получателя).

Пример:

ch := make(chan int, 2) // Буфер на 2 элемента

ch <- 1 // Отправили, места еще есть, идем дальше
ch <- 2 // Отправили, буфер полон
fmt.Println("Main: отправил 2 числа, не дожидаясь получателя")

// Теперь кто-то другой может забрать данные позже
go func() {
val := <-ch
fmt.Println("Горутина: получила", val)
}()

Сравнение: когда что использовать?

ХарактеристикаНебуферизированныйБуферизированный
СинхронизацияСтрогая (Rendezvous)Свободная (Асинхронная)
ПамятьНе требуетТребует (выделяется под буфер)
БезопасностьГарантирует доставкуМожет привести к переполнению, если не читать быстро
ПрименениеКогда нужно точно знать, что задача выполненаКогда нужно сгладить пики нагрузки (очередь)

Совет:

  • Используйте небуферизированные каналы по умолчанию — это самый безопасный способ избежать "гонок" и убедиться, что процессы идут нога в ногу.
  • Используйте буферизированные каналы как «очередь» (например, если отправитель работает быстрее, чем получатель, и вы хотите избежать простоев отправителя). Будьте осторожны: если буфер бесконечно растет, а никто не читает, вы съедите всю память!