Типы каналов
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) | Свободная (Асинхронная) |
| Память | Не требует | Требует (выделяется под буфер) |
| Безопасность | Гарантирует доставку | Может привести к переполнению, если не читать быстро |
| Применение | Когда нужно точно знать, что задача выполнена | Когда нужно сгладить пики нагрузки (очередь) |
Совет:
- Используйте небуферизированные каналы по умолчанию — это самый безопасный способ избежать "гонок" и убедиться, что процессы идут нога в ногу.
- Используйте буферизированные каналы как «очередь» (например, если отправитель работает быстрее, чем получатель, и вы хотите избежать простоев отправителя). Будьте осторожны: если буфер бесконечно растет, а никто не читает, вы съедите всю память!