🔄 Каналы (Channels) в Go
Каналы - это механизм связи между горутинами, позволяющий безопасно передавать данные, синхронизировать выполнение и строить конкурентный код без гонок данных.
📦 Создание канала
ch := make(chan int)
Так создаётся небуферизированный канал, который блокирует отправителя до момента чтения.
📤 Отправка и 📥 приём данных
go func() { ch <- 1 }()
value := <-ch
fmt.Println(value) // 1
ch <- x- отправка данных<-ch- получение данных
🔁 Несколько отправителей
ch := make(chan int)
go func() { ch <- 1 }()
go func() { ch <- 2 }()
go func() { ch <- 3 }()
a := <-ch
b := <-ch
c := <-ch
fmt.Println(a, b, c)
Порядок зависит от планировщика.
🧺 Буферизированные каналы
Буфер позволяет отправлять данные без немедленного чтения:
ch := make(chan int, 2)
ch <- 1
ch <- 2
go func() { ch <- 3 }()
fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
fmt.Println(<-ch) // 3
Поведение - FIFO.
🔀 Однонаправленные каналы
Можно ограничить направление:
func worker(in <-chan int, out chan<- int) {
x := <-in
out <- x * 2
}
<-chan- только чтениеchan<-- только запись
🎛 Select: работа с несколькими каналами
select даёт возможность слушать несколько каналов:
one := make(chan int)
two := make(chan int)
for {
select {
case o := <-one:
fmt.Println("one:", o)
case t := <-two:
fmt.Println("two:", t)
default:
fmt.Println("no data")
time.Sleep(50 * time.Millisecond)
}
}
⏳ Таймауты через select + time.After
ch := make(chan int)
for {
select {
case v := <-ch:
fmt.Println("received:", v)
case <-time.After(300 * time.Millisecond):
fmt.Println("timed out")
return
}
}
📚 Итоги
- Каналы --- двусторонние трубы для передачи данных
- Небуферизированные каналы блокируют отправку/получение
- Буферизированные дают очередь FIFO
- Select работает с несколькими каналами одновременно
- Можно делать однонаправленные каналы
- Через time.After легко задавать таймауты