Menu
Coddy logo textTech

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

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

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

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

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

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

С буферизованным каналом операции отправки блокируются только тогда, когда буфер заполнен, а операции получения — только когда буфер пуст:

ch := make(chan string, 2)

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

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

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

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

challenge icon

Задание

Легко

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

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

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

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

    Реализуйте две функции:

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

    Считайте емкость буфера, затем количество элементов, а затем ID и значение каждого элемента. Создайте буферизованный канал с указанной емкостью. Сначала запустите Producer (не как горутину), чтобы заполнить буфер, затем запустите Consumer для обработки всех элементов. Выведите каждый результат потребления на отдельной строке.

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

  • Строка 1: Емкость буфера (целое число)
  • Строка 2: Количество элементов (целое число)
  • Следующие строки: Для каждого элемента две строки — ID элемента (целое число), затем его значение (строка)

Например, при вводе:

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

Обратите внимание, что при емкости буфера 3 производитель может отправить все 3 элемента без блокировки, и буфер показывает 3 ожидающих элемента до начала работы потребителя. Если бы буфер был меньше количества элементов, производитель заблокировался бы в ожидании свободного места.

Шпаргалка

Каналы в Go могут быть небуферизованными (емкость 0) или буферизованными (с внутренним хранилищем).

Создавайте каналы с помощью make:

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

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

Поведение буферизованного канала:

  • Отправка блокируется только тогда, когда буфер заполнен
  • Получение блокируется только тогда, когда буфер пуст
ch := make(chan string, 2)

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

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

Проверка состояния буфера:

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

Когда использовать:

  • Небуферизованные: Строгая синхронизация — отправитель знает, что получатель принял значение
  • Буферизованные: Разделение времени работы отправителя и получателя, когда они работают с разной скоростью

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

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 (не как горутину), чтобы заполнить буфер

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

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

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

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