Уроки модуля (2/5)
Каналы: типизированное общение
Каналы – это идиоматический механизм связи между горутины. Философия Го (Роб Пайк):
«Не общайтесь, делясь воспоминаниями; делитесь воспоминаниями, общаясь».
Канал — это типизированная очередь FIFO, типобезопасная и безопасная для одновременной обработки.
использовать. В сочетании с go это основной строительный блок Go.
параллелизм.
Создать, отправить, получить
ch := make(chan int) // canale di int, unbuffered
go func() {
ch <- 42 // invia (può bloccare)
}()
v := <-ch // ricevi (può bloccare)
fmt.Println(v) // 42Синтаксис:
make(chan T)илиmake(chan T, N)для буферизации.ch <- vотправляет.v := <-chполучает.v, ok := <-chполучает с флагом (ok = false, если канал закрыт И пуст).
Без буферизации: рандеву
Небуферизованный канал (емкость 0) блокирует:
- отправителя до готовности получателя;
- получатель, пока отправитель не будет готов.
Это синхронное назначение: полезно в качестве «сигнала» или для гарантии что значение было доставлено.
Буферизовано: очередь до N
ch := make(chan int, 3)
ch <- 1 // non blocca
ch <- 2 // non blocca
ch <- 3 // non blocca
ch <- 4 // BLOCCA: il buffer è pienoОтправитель блокируется только тогда, когда буфер заполнен; только приемник когда он пуст.
близко и на расстоянии
Отправитель закрывает канал, когда значений больше нет:
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for v := range ch { // termina quando ch è chiuso E vuoto
fmt.Println(v)
}for range по каналу выполняется до тех пор, пока канал не будет закрыт (и опорожнен).
Это самый чистый шаблон «поедать все, что попадает».
Направленность в подписях
Параметры функции могут ограничивать направление:
func producer(out chan<- int) { // solo invio
out <- 1
close(out)
}
func consumer(in <-chan int) { // solo ricezione
for v := range in {
fmt.Println(v)
}
}chan<- T = только отправка, <-chan T = только получение. Двунаправленный
канал chan T можно передать любому из них (неявное преобразование). Это
улучшает ясность и предотвращает ошибки.
Общие шаблоны (предварительный просмотр)
- Рабочий пул: N горутин потребляют данные из
jobs <-chan T, производятresults chan<- R. - Конвейер: этапы, соединенные каналами.
- Сигнал завершения: канал
chan struct{}закрыт для сигнала «стоп».
done := make(chan struct{})
go func() {
<-done // blocca finché qualcuno chiude done
cleanup()
}()
close(done) // segnale di stopchan struct{} — это «сигнальный» канал: ноль байт полезной нагрузки, всего лишь
синхронизация.
Попробуйте это
Создайте небуферизованный int-канал ch, отправьте 42 из горутины, получите в main и распечатайте его.
Показать подсказку
Без буфера отправитель и получатель синхронизируются.
Решение доступно после 3 попыток
Отправьте 1,2,3 через буферизованный канал, закройте его, затем распечатайте все с помощью for-range.
Показать подсказку
`for range` по каналу заканчивается, когда канал закрывается и опорожняется.
Решение доступно после 3 попыток
Что делает НЕБУФЕРИРОВАННЫЙ канал, когда отправитель отправляет, но получатель не готов?
ch := make(chan int)
ch <- 1 // ?Резюме
- Канал = типизированная очередь FIFO, безопасная между горутинами.
- Без буферизации: рандеву (синхронизация отправителя и получателя).
- Буферизовано: развязка, блокируется только при заполнении/пустоте.
close(ch): только отправитель;for rangeпотребляет до закрытия.- Направленность:
chan<- T(только отправка),<-chan T(только прием). chan struct{}как сигнальный канал для чистой синхронизации.