Каналы в Go
Канал (channel) — это типизированная очередь, через которую горутины могут безопасно передавать данные.
Простыми словами: канал можно представить как безопасную очередь, в которую одна горутина кладёт данные, а другая — забирает.
Базовые действия:
// создание канала
ch := make(chan int)
// операции
ch <- 10 // отправка в канал
x := <-ch // получение из канала
Виды каналов
- Буферизированные
- Небуферизированные
Рассмотрим каждый канал по отдельности. Начнем с небуферизированного канала.
Небуферизированные каналы
Как их создать — уже рассмотрели наверху. Какие у них особенности?
- не имеют внутреннего хранилища (буфера)
- отправка блокируется, пока значение не будет прочитано
- обеспечивают строгую синхронизацию между горутинами
Используются по умолчанию и подходят в большинстве случаев.
Буферизованные каналы
От небуферизированных они отличаются только тем, что у них есть буфер. И только. Пример создания:
// создание канала
ch := make(chan int, 3)
// 3 - буфер из 3х элементов
- имеют буфер фиксированного размера (в нашем случае — 3)
- позволяют отправлять данные, пока буфер не заполнен (т.е. отправители не будут блокироваться до тех пор, пока буфер не переполнится).
- уменьшают жёсткую зависимость между отправителем и получателем
Полезны, когда скорость работы горутин отличается.
Примеры
Небуферизированный канал
ch := make(chan string)
go func() {
ch <- "Сообщение из горутины"
}()
fmt.Println(<-ch)
Здесь мы создали канал, в который кладем текстовое значение "Сообщение из горутины" и читаем в основной горутине (main) командой <-ch. Основная горутина будет ждать, пока дочерняя отправит сообщение.
Буферизованный канал
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
Создали канал с буфером == 2. Если придут 3 горутины, которые хотят положить данные в этот канал, то первые две положат в него данные, а 3-я будет заморожена, пока буфер не освободится (т.е. пока читатель не прочитатет оттуда какое-то значение, чтобы ячейа буфера освободилась, чтобы 3-я горутина смогла положить туда свои данные).
Сигнал о завершении работы
done := make(chan struct{})
go func() {
fmt.Println("Выполняю работу")
done <- struct{}{}
}()
<-done
fmt.Println("Работа завершена")
Классический и очень популярный паттерн для ожидания завершения горутины. Примерно таким образом я использовал каналы взамен sync.Mutex, для своих учебных задач.
Итог
Каналы — основной способ общения между горутинами в Go. Чаще всего используются:
- небуферизованные каналы — для синхронизации
- буферизованные каналы — для асинхронной передачи данных
Данная статья не раскрывает полностью возможности и способы использования каналов в Go, а лишь описывает их поверхностно.
Комментарии
0
Ты: ...
Пока нет комментариев. Будь первым.