Каналы и взаимодействие
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 60 из 107.
Каналы (Channels) — это основной механизм Go для взаимодействия между горутинами (goroutines). Вместо того чтобы напрямую разделять память, горутины отправляют и получают значения через каналы, следуя философии Go: «Не общайтесь через разделяемую память; разделяйте память через общение».
Создайте канал с помощью функции make и ключевого слова chan:
messages := make(chan string)Используйте оператор стрелки <- для отправки и получения значений. Стрелка указывает направление потока данных:
func main() {
messages := make(chan string)
go func() {
messages <- "hello" // отправка в канал
}()
msg := <-messages // получение из канала
fmt.Println(msg) // Вывод: hello
}По умолчанию операции отправки и получения блокируются до тех пор, пока другая сторона не будет готова. Когда горутина отправляет "hello", она ждет, пока main не будет готова к приему. Это блокирующее поведение обеспечивает встроенную синхронизацию — time.Sleep не требуется.
Вы можете закрыть канал, чтобы просигнализировать о том, что больше значений отправлено не будет. Получатели могут это обнаружить:
close(messages)
msg, ok := <-messages
if !ok {
fmt.Println("Channel closed")
}Каналы типизированы — chan string может передавать только строки. Эта типобезопасность гарантирует, что горутины взаимодействуют с правильными типами данных, позволяя обнаруживать ошибки на этапе компиляции, а не во время выполнения.
Задание
ЛегкоДавайте создадим систему ретрансляции сообщений, которая демонстрирует, как горутины взаимодействуют через каналы. Вы создадите конвейер (pipeline), в котором сообщения передаются между различными этапами обработки, причем каждый этап выполняется одновременно.
Вы организуете свой код в двух файлах:
relay.go: Определите ваш конвейер обработки сообщений.Создайте структуру
Messageс полямиID(int) иContent(string).Реализуйте три функции, представляющие различные этапы вашего конвейера:
Producer(messages []Message, out chan Message)— принимает срез сообщений и отправляет каждое из них в выходной канал. После отправки всех сообщений закройте канал, чтобы просигнализировать о завершении.Transformer(in chan Message, out chan Message)— получает сообщения из входного канала, преобразует содержимое каждого сообщения в верхний регистр и отправляет преобразованное сообщение в выходной канал. Когда входной канал закрыт (обнаруживается с помощью формы получения с двумя значениями), закройте выходной канал.Consumer(in chan Message) []string— получает все сообщения из входного канала и собирает их в срез отформатированных строк. Каждая строка должна соответствовать формату:"Message %d: %s". Верните срез, когда канал будет закрыт.
main.go: Настройте конвейер и организуйте параллельный поток сообщений.Прочитайте количество сообщений, затем прочитайте ID и содержимое каждого сообщения. Создайте сообщения и настройте два канала для соединения трех этапов конвейера. Запустите Producer и Transformer как горутины, а затем запустите Consumer в основной горутине для сбора результатов. Выведите каждый результат на отдельной строке.
Будут предоставлены следующие входные данные:
- Строка 1: Количество сообщений (целое число)
- Следующие строки: Для каждого сообщения две строки — ID сообщения (целое число), затем его содержимое (строка)
Например, при вводе:
2
1
hello world
2
go channelsВаш вывод должен быть:
Message 1: HELLO WORLD
Message 2: GO CHANNELSКонвейер работает следующим образом: Producer отправляет сообщения → Transformer преобразует их в верхний регистр → Consumer собирает и форматирует результаты. Каждая стрелка представляет собой канал, соединяющий параллельные этапы. Используйте пакет strings для преобразования в верхний регистр.
Шпаргалка
Каналы — это основной механизм Go для взаимодействия между горутинами. Создайте канал с помощью make и ключевого слова chan:
messages := make(chan string)Используйте оператор стрелки <- для отправки и получения значений. Стрелка указывает направление потока данных:
messages <- "hello" // отправка в канал
msg := <-messages // получение из каналаПо умолчанию отправка и получение блокируются до тех пор, пока другая сторона не будет готова, обеспечивая встроенную синхронизацию:
func main() {
messages := make(chan string)
go func() {
messages <- "hello" // отправка в канал
}()
msg := <-messages // получение из канала
fmt.Println(msg) // Output: hello
}Закройте канал, чтобы просигнализировать о том, что больше значений отправлено не будет. Получатели могут обнаружить закрытие, используя форму получения с двумя значениями:
close(messages)
msg, ok := <-messages
if !ok {
fmt.Println("Channel closed")
}Каналы типизированы — chan string может передавать только строки, обеспечивая безопасность типов на этапе компиляции.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Чтение количества сообщений
nLine, _ := reader.ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(nLine))
// Чтение сообщений
messages := make([]Message, n)
for i := 0; i < n; i++ {
idLine, _ := reader.ReadString('\n')
id, _ := strconv.Atoi(strings.TrimSpace(idLine))
contentLine, _ := reader.ReadString('\n')
content := strings.TrimSpace(contentLine)
messages[i] = Message{ID: id, Content: content}
}
// TODO: Создать два канала для соединения этапов конвейера
// TODO: Запустить Producer как горутину
// TODO: Запустить Transformer как горутину
// TODO: Запустить Consumer в основной горутине и собрать результаты
// TODO: Вывести каждый результат на отдельной строке
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool