Menu
Coddy logo textTech

Каналы и взаимодействие

Часть раздела Объектно-ориентированное программирование путешествия по 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 может передавать только строки. Эта типобезопасность гарантирует, что горутины взаимодействуют с правильными типами данных, позволяя обнаруживать ошибки на этапе компиляции, а не во время выполнения.

challenge icon

Задание

Легко

Давайте создадим систему ретрансляции сообщений, которая демонстрирует, как горутины взаимодействуют через каналы. Вы создадите конвейер (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: Вывести каждый результат на отдельной строке
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование