0Pricing
Go Academy · Lección

Channels con búfer

Capacidad, cierre de channels e iteración con range sobre un channel

Channels con búfer es una lección gratuita de Go Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Go Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Go Academy incluye 4 lecciones en total.

¿Qué es un canal con búfer?

Un canal con búfer tiene una capacidad; los envíos no se bloquean mientras haya espacio en el búfer. El canal actúa como una cola:

package main
import "fmt"

func main() {
    ch := make(chan int, 3)  // capacity 3

    ch <- 1  // does not block — buffer has room
    ch <- 2  // does not block
    ch <- 3  // does not block
    // ch <- 4  // would block — buffer full

    fmt.Println(<-ch) // 1
    fmt.Println(<-ch) // 2
    fmt.Println(<-ch) // 3
}

El canal con búfer como cola

Los canales con búfer implementan una cola FIFO entre goroutines:

package main
import ("fmt"; "sync")

func producer(ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 1; i <= 5; i++ {
        ch <- i
        fmt.Printf("produced %d\n", i)
    }
}

func main() {
    ch := make(chan int, 2)  // buffer 2 — producer runs ahead
    var wg sync.WaitGroup
    wg.Add(1)
    go producer(ch, &wg)
    wg.Wait()
    close(ch)
    for v := range ch { fmt.Printf("consumed %d\n", v) }
}

len() y cap() en canales

Consulte el tamaño actual y la capacidad de un canal con búfer:

package main
import "fmt"

func main() {
    ch := make(chan string, 5)
    ch <- "a"
    ch <- "b"
    ch <- "c"

    fmt.Println("cap:", cap(ch))  // 5
    fmt.Println("len:", len(ch))  // 3 — items currently buffered

    <-ch
    fmt.Println("len:", len(ch))  // 2
}

Desacoplar el productor y el consumidor

Un búfer desacopla la velocidad del productor de la del consumidor. El productor puede adelantarse una cantidad equivalente al tamaño del búfer antes de bloquearse:

package main
import ("fmt"; "sync"; "time")

func produce(ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 5; i++ {
        ch <- i
    }
}

func consume(ch <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for v := range ch {
        time.Sleep(10 * time.Millisecond)  // slower consumer
        fmt.Println("consumed:", v)
    }
}

func main() {
    ch := make(chan int, 3)
    var wg sync.WaitGroup
    wg.Add(2)
    go produce(ch, &wg)
    go consume(ch, &wg) // BUG: produce closes before consume sees all
    // In practice, coordinate close carefully
    fmt.Println("decoupled")
}

Semáforo con un canal con búfer

Un canal con búfer de capacidad N puede limitar a N el número de goroutines simultáneas: es un semáforo sencillo:

package main
import ("fmt"; "sync"; "time")

func main() {
    sem := make(chan struct{}, 3)  // max 3 concurrent
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        i := i
        go func() {
            defer wg.Done()
            sem <- struct{}{}    // acquire
            defer func() { <-sem }()  // release
            fmt.Printf("job %d running\n", i)
            time.Sleep(50 * time.Millisecond)
        }()
    }
    wg.Wait()
}

Cerrar un canal con búfer

Al cerrar un canal con búfer, los receptores pueden consumir los valores restantes del búfer antes de detectar el cierre:

package main
import "fmt"

func main() {
    ch := make(chan int, 5)
    for i := 1; i <= 3; i++ { ch <- i }
    close(ch)

    // Drain all buffered values after close:
    for v := range ch {
        fmt.Println(v)  // 1, 2, 3
    }
    // range exits after channel closed and drained
}

Con búfer frente a sin búfer: cuándo elegir cada uno

Guía para decidir:

  • Sin búfer: entrega síncrona garantizada, orden estricto y sincronización mediante rendezvous
  • Con búfer: desacopla las velocidades del productor y el consumidor, reduce los bloqueos, implementa semáforos y recopila N resultados
  • Use canales sin búfer de forma predeterminada; agregue un búfer solo cuando pueda justificar su tamaño
  • Un búfer demasiado pequeño → sigue provocando bloqueos; uno demasiado grande → oculta errores

Recopilar resultados con un canal con búfer

Use un canal con búfer para recopilar resultados de goroutines paralelas sin bloquearlas:

package main
import ("fmt"; "sync")

func square(n int) int { return n * n }

func main() {
    nums := []int{1, 2, 3, 4, 5}
    results := make(chan int, len(nums))  // buffer = goroutine count
    var wg sync.WaitGroup

    for _, n := range nums {
        wg.Add(1)
        n := n
        go func() {
            defer wg.Done()
            results <- square(n)
        }()
    }
    wg.Wait()
    close(results)

    for r := range results { fmt.Println(r) }
}

Fuga de goroutines con un búfer lleno

Si el búfer está lleno y no existe ningún lector, el emisor se bloquea para siempre: se produce una fuga de goroutines:

package main
import ("fmt"; "time"; "runtime")

func main() {
    ch := make(chan int, 2)
    // Start goroutine that fills buffer and then blocks
    go func() {
        ch <- 1
        ch <- 2
        ch <- 3  // blocks: buffer full, no receiver
        fmt.Println("never reached")
    }()
    time.Sleep(50 * time.Millisecond)
    fmt.Println("goroutines:", runtime.NumGoroutine()) // 2 — leaked
}

time.After como canal con búfer

time.After devuelve un <-chan time.Time con un búfer de capacidad 1. El runtime le envía un valor una vez transcurrida la duración y después se recolecta como basura:

package main
import ("fmt"; "time")

func doWithTimeout(work func() string) (string, bool) {
    result := make(chan string, 1)
    go func() { result <- work() }()
    select {
    case r := <-result:
        return r, true
    case <-time.After(100 * time.Millisecond):
        return "", false
    }
}

func main() {
    r, ok := doWithTimeout(func() string {
        time.Sleep(50 * time.Millisecond)
        return "done"
    })
    fmt.Println(r, ok)
}

Comprobación rápida

¿Qué ocurre cuando envía a un canal con búfer que está lleno?

Recapitulación: canales con búfer

Resumen:

  • make(chan T, N) crea un canal con búfer de capacidad N
  • Los envíos se bloquean solo cuando el búfer está lleno; las recepciones, solo cuando está vacío
  • len(ch) — elementos actuales; cap(ch) — capacidad
  • Úselos para desacoplar productores y consumidores, recopilar resultados paralelos e implementar semáforos
  • El cierre permite consumir los valores restantes antes de EOF; range ch lo gestiona automáticamente

Preguntas frecuentes

¿La lección «Channels con búfer» es gratis?

Sí — el texto completo de «Channels con búfer» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Go Academy, actualiza a CoddyKit PRO. El curso de Go Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Channels con búfer»?

Capacidad, cierre de channels e iteración con range sobre un channel Practicas Go Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Go Academy?

No se requiere experiencia previa. Go Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Channels con búfer»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Go Academy?

Sí. Cada lección de Go Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Lanzamiento de goroutines
  2. Channels sin búfer
  3. Channels con búfer
  4. Dirección de channels y patrones de pipelines
← Volver a Go Academy