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 chlo 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
- Lanzamiento de goroutines
- Channels sin búfer
- Channels con búfer
- Dirección de channels y patrones de pipelines