Menu
Coddy logo textTech

Channels con buffer vs. sin buffer

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 61 de 107.

Los canales que hemos usado hasta ahora no tienen búfer. No tienen capacidad para almacenar valores. Una operación de envío se bloquea hasta que otra goroutine recibe, y viceversa. Los canales con búfer añaden almacenamiento interno, lo que permite que los envíos se completen sin un receptor inmediato.

Crea un canal con búfer pasando una capacidad a make:

// Sin búfer - capacidad 0
unbuffered := make(chan int)

// Con búfer - capacidad 3
buffered := make(chan int, 3)

Con un canal con búfer, los envíos solo se bloquean cuando el búfer está lleno, y las recepciones solo se bloquean cuando el búfer está vacío:

ch := make(chan string, 2)

ch <- "first"   // no bloquea - el buffer tiene espacio
ch <- "second"  // no bloquea - el buffer tiene espacio
// ch <- "third" bloquearía - el buffer está lleno

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

Usa len(ch) para comprobar cuántos elementos hay actualmente en el búfer y cap(ch) para la capacidad total.

Cuándo usar cada uno: Los canales sin búfer proporcionan una sincronización estrecha: el remitente sabe que el receptor recibió el valor. Los canales con búfer desacoplan los tiempos del remitente y del receptor, lo que resulta útil cuando los productores y los consumidores trabajan a distintas velocidades. Sin embargo, los canales con búfer pueden ocultar errores de sincronización, así que prefiere los canales sin búfer a menos que tengas una razón específica para usar búferes.

challenge icon

Desafío

Fácil

Construyamos un procesador por lotes que demuestre la diferencia entre los canales buffered y no buffered. Crearás un sistema en el que un productor envía items a un procesador, utilizando canales buffered para desacoplar sus tiempos y permitir que el productor se adelante.

Organizarás tu código en dos archivos:

  • processor.go: Define la lógica de procesamiento por lotes utilizando canales.

    Crea una estructura Item con campos ID (int) y Value (string).

    Implementa dos funciones:

    • Producer(items []Item, out chan Item) - Envía cada item al canal de salida. Después de enviar cada item, imprime: Produced item [ID]. Después de enviar todos los items, imprime el número actual de items que esperan en el buffer utilizando len(out) con el formato: Buffer has [count] items. Después, cierra el canal.
    • Consumer(in chan Item) []string - Recibe items del canal de entrada y los recopila en un slice de cadenas formateadas. Para cada item recibido, la cadena debe ser: Consumed: [ID] - [Value]. Devuelve el slice cuando el canal se cierra.
  • main.go: Configura el canal buffered y coordina el productor y el consumidor.

    Lee la capacidad del buffer, después el number de items, seguido del ID y el valor de cada item. Crea un canal buffered con la capacidad especificada. Ejecuta primero Producer (no como una goroutine) para llenar el buffer y, después, ejecuta Consumer para procesar todos los items. Imprime cada resultado consumed en una línea separada.

Se proporcionarán las siguientes entradas:

  • Línea 1: Capacidad del buffer (entero)
  • Línea 2: Number de items (entero)
  • Líneas siguientes: Para cada item, dos líneas: el ID del item (entero), después su valor (cadena)

Por ejemplo, dada la siguiente entrada:

3
3
1
apple
2
banana
3
cherry

Tu salida debería ser:

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

Observa que, con una capacidad del buffer de 3, el productor puede enviar los 3 items sin bloquearse, y el buffer muestra 3 items esperando antes de que comience el consumidor. Si el buffer fuera más pequeño que el number de items, el productor se bloquearía esperando espacio.

Pruébalo tú mismo

package main

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

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

	// Leer la capacidad del buffer
	line, _ := reader.ReadString('\n')
	bufferCapacity, _ := strconv.Atoi(strings.TrimSpace(line))

	// Leer el número de elementos
	line, _ = reader.ReadString('\n')
	numItems, _ := strconv.Atoi(strings.TrimSpace(line))

	// Leer el ID y el valor de cada elemento
	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: Crear un canal con buffer con la capacidad especificada

	// TODO: Ejecutar el Producer (no como una goroutine) para llenar el buffer

	// TODO: Ejecutar el Consumer para procesar todos los elementos

	// TODO: Imprimir cada resultado consumido en una línea separada
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Go online