Menu
Coddy logo textTech

Буферизованные и небуферизованные каналы

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 61 из 107.

Каналы, которые мы использовали до сих пор, являются unbuffered. У них нет capacity для хранения значений. Операция отправки блокируется, пока другая goroutine не выполнит получение, и наоборот. Buffered channels добавляют внутреннее хранилище, позволяя операциям отправки завершаться без немедленного получателя.

Создайте буферизованный канал, передав ёмкость в make:

// Небуферизованный - ёмкость 0
unbuffered := make(chan int)

// Буферизованный - ёмкость 3
buffered := make(chan int, 3)

С buffered channel отправка блокирует выполнение только когда буфер full, а получение — только когда буфер empty:

ch := make(chan string, 2)

ch <- "first"   // не блокирует - в буфере есть место
ch <- "second"  // не блокирует - в буфере есть место
// ch <- "third" заблокировало бы - буфер заполнен

fmt.Println(<-ch)  // "first"
fmt.Println(<-ch)  // "second"

Используйте len(ch), чтобы проверить, сколько элементов сейчас находится в буфере, а cap(ch) — чтобы узнать общую capacity.

Когда использовать each: Небуферизованные каналы обеспечивают тесную синхронизацию: отправитель знает, что получатель получил значение. Buffered каналы разделяют моменты работы отправителя и получателя, что полезно, когда производители и потребители работают с разной скоростью. Однако Buffered каналы могут скрывать ошибки синхронизации, поэтому предпочитайте небуферизованные каналы, если у вас нет конкретной причины использовать буферизацию.

challenge icon

Задание

Легко

Давайте создадим пакетный процессор, демонстрирующий разницу между buffered и небуферизованными каналами. Вы создадите систему, в которой Producer отправляет items процессору, используя buffered каналы для разделения их временных интервалов и позволяя Producer работать с опережением.

Вы организуете код в двух файлах:

  • processor.go: Определите логику пакетной обработки с использованием каналов.

    Создайте структуру Item с полями ID (int) и Value (string).

    Implement две функции:

    • Producer(items []Item, out chan Item) — отправляет каждый item в выходной канал. После отправки каждого item выведите: Produced item [ID]. После отправки всех items выведите текущее number items, ожидающих в buffer, используя len(out), в формате: Buffer has [count] items. Затем закройте канал.
    • Consumer(in chan Item) []string — получает items из входного канала и собирает их в срез отформатированных строк. Для каждого полученного item строка должна иметь вид: Consumed: [ID] - [Value]. Верните срез, когда канал закрыт.
  • main.go: Настройте buffered канал и скоординируйте Producer и Consumer.

    Считайте capacity buffer, затем number items, после чего ID и value каждого item. Создайте buffered канал с указанной capacity. Сначала запустите Producer (не как goroutine), чтобы заполнить buffer, затем запустите Consumer для обработки всех items. Выведите каждый результат обработки на отдельной строке.

Будут предоставлены следующие входные данные:

  • Строка 1: capacity buffer (целое число)
  • Строка 2: number items (целое число)
  • Следующие строки: для каждого item две строки — ID item (целое число), затем его value (строка)

Например, если даны:

3
3
1
apple
2
banana
3
cherry

Ваш вывод должен быть таким:

Produced item 1
Produced item 2
Produced item 3
Buffer has 3 items
Consumed: 1 - apple
Consumed: 2 - banana
Consumed: 3 - cherry

Обратите внимание: при capacity buffer, равной 3, Producer может отправить все 3 items без блокировки, и buffer показывает 3 ожидающих item до запуска Consumer. Если buffer меньше number items, Producer будет заблокирован в ожидании свободного места.

Попробуйте сами

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Прочитать ёмкость буфера
	line, _ := reader.ReadString('\n')
	bufferCapacity, _ := strconv.Atoi(strings.TrimSpace(line))

	// Прочитать количество элементов
	line, _ = reader.ReadString('\n')
	numItems, _ := strconv.Atoi(strings.TrimSpace(line))

	// Прочитать ID и значение каждого элемента
	items := make([]Item, numItems)
	for i := 0; i < numItems; i++ {
		line, _ = reader.ReadString('\n')
		id, _ := strconv.Atoi(strings.TrimSpace(line))
		line, _ = reader.ReadString('\n')
		value := strings.TrimSpace(line)
		items[i] = Item{ID: id, Value: value}
	}

	// TODO: Создать буферизованный канал с указанной ёмкостью

	// TODO: Запустить Producer (не как goroutine) для заполнения буфера

	// TODO: Запустить Consumer для обработки всех элементов

	// TODO: Вывести каждый потреблённый результат на отдельной строке
}
quiz iconПроверьте себя

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор Go