Canales: Reglas de Búfer, Sin Búfer y Cierre
Los canales transportan valores y sincronización entre goroutines.
Busca en todas las páginas de la documentación
Los canales transportan valores y sincronización entre goroutines.
Elegir la capacidad con búfer o sin búfer y seguir las reglas de propiedad de cierre evita bloqueos y pánicos en los pipelines de producción.
Un canal sin búfer sincroniza a los compañeros: el remitente se bloquea hasta que el receptor está listo.
Un canal con búfer almacena hasta cap valores sin un receptor esperando.
Cerrar un canal transmite "no más envíos" a los receptores; solo los remitentes deben cerrar.
range sobre un canal lo vacía hasta que se cierra; la recepción de dos valores v, ok := <-ch detecta el cierre explícitamente.
Tarjeta de referencia rápida - lista para copiar y pegar.
package main
import "fmt"
func main() {
ch := make(chan int, 2) // con búfer
ch <- 1
ch <- 2
close(ch)
for v := range ch {
fmt.Println(v)
}
v, ok := <-ch
fmt.Println(v, ok) // 0 false después de vaciar el cierre
}Cuándo usar esto:
range.package main
import (
"fmt"
"sync"
)
func producer(out chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 1; i <= 5; i++ {
out <- i
}
close(out) // el remitente cierra
}
func consumer(in <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for v := range in {
fmt.Println("got", v)
}
}
func main() {
ch := make(chan int) // sin búfer - traspaso de sincronización
var wg sync.WaitGroup
wg.Add(2)
go producer(ch, &wg)
go consumer(ch, &wg)
wg.Wait()
}Lo que esto demuestra:
chan<- y <-chan documentan el uso de solo envío/solo recepción.range en el consumidor termine.WaitGroup espera a que ambos lados terminen.close(ch) establece la bandera de cerrado; los envíos futuros obtienen el valor cero con ok == false.len(ch) y cap(ch) informan sobre los elementos en cola y la capacidad.| Tipo | El envío se bloquea cuando | La recepción se bloquea cuando | Fuerza de sincronización |
|---|---|---|---|
| Sin búfer | No hay receptor listo | No hay remitente listo | Fuerte (encuentro) |
| Con búfer | Búfer lleno | Búfer vacío | Más débil hasta que está lleno |
| Regla | Razón |
|---|---|
| El remitente cierra | Los receptores no deben señalar la finalización del productor |
| Cerrar una vez | Pánicos de doble cierre |
Los receptores detectan a través de ok o range | Cierre limpio de los bucles del consumidor |
| No cerrar canales de trabajo anticipadamente | Los consumidores aún pueden esperar valores |
// Fan-in: una goroutine cierra después de que todos los remitentes terminan
func fanIn(inputs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(inputs))
for _, in := range inputs {
go func(c <-chan int) {
defer wg.Done()
for v := range c {
out <- v
}
}(in)
}
go func() {
wg.Wait()
close(out)
}()
return out
}sync.Once para proteger el cierre.context, select con tiempo de espera o búfer.close o cancelar upstream.WaitGroup y luego cerrar).
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Canal sin búfer | Necesita traspaso estricto | Alto rendimiento con tasas dispares |
| Canal con búfer | Absorción de ráfagas | Necesita señales de retroalimentación inmediatas |
sync.Mutex + cola de slice | Cola compartida en memoria | La transferencia de propiedad entre goroutines es más clara con canales |
Solo context | Señalización de cancelación | Pasar elementos de trabajo (usar canales) |
Comienza con 0 (sin búfer) o un múltiplo pequeño del número de trabajadores.
Realiza pruebas de rendimiento y observa la profundidad de la cola bajo carga; los búferes sobredimensionados ocultan la sobrecarga.
Sí, los envíos son seguros concurrentemente.
Coordina el cierre para que solo una goroutine cierre después de que todos los remitentes terminen.
Se bloquea para siempre.
Usa un canal nil en select para deshabilitar un caso dinámicamente.
Los receptores bloqueados para siempre mantienen vivos a los remitentes independientemente del cierre.
El cierre es para la semántica y la terminación de range, no principalmente para la GC.
Técnicamente posible en ejemplos sencillos; en pipelines rompe la propiedad.
Los receptores deben salir al cerrar, no iniciarlo.
nil - las operaciones en canales nil se bloquean para siempre (útil en trucos de select).
chan<- T y <-chan T en las firmas de funciones documentan la intención y previenen recepciones/envíos accidentales en tiempo de compilación.
Sí, cada recepción copia.
Envía punteros si modificas structs compartidos (con sincronización).
Los receptores vacían los valores con búfer después del cierre, y luego obtienen ok == false.
Un envío en un canal ocurre antes de que la recepción correspondiente se complete.
Ese borde es parte del modelo de memoria de Go.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado GC Green Tea, go fix modernizers - verificar parche en la compilación), chi (última - verificar en la compilación), gin (última - verificar en la compilación), echo (última - verificar en la compilación), google.golang.org/grpc (última - verificar en la compilación), sigs.k8s.io/controller-runtime (última - verificar en la compilación), kubebuilder (última - verificar en la compilación), tinygo (última - verificar objetivos de placa en la compilación), wazero (última - verificar en la compilación) y golangci-lint (última - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 19 jul 2026