Historia de la pregunta: Los canales son una de las conceptos fundamentales del modelo CSP en Go. Están destinados a intercambiar datos entre gorutinas. Trabajar con canales requiere especial cuidado para evitar deadlocks, fugas y pánicos al cerrarlos.
Problema: Cerrar y el uso posterior de canales a menudo conduce a pánicos (panic: send on closed channel). Trabajar con canales nil o intentar leer de un canal cerrado sin comprobar puede dar resultados inesperados. Diferentes gorutinas pueden escribir y leer en un mismo canal simultáneamente, lo que requiere una lógica de ciclo de vida bien definida.
Solución: El canal siempre debe cerrarse solo desde el lado donde no habrá más escrituras (generalmente el productor). Después de cerrarlo, se puede leer valores del canal hasta que se agote, pero no se puede escribir — habrá panic. Es conveniente comprobar el cierre a través del segundo parámetro devuelto del canal:
Ejemplo de código:
ch := make(chan int) go func() { for i := 0; i < 5; i++ { ch <- i } close(ch) }() for v := range ch { fmt.Println(v) }
Características clave:
1. ¿Es necesario cerrar el canal si solo una gorutina lo lee y nadie más espera valores adicionales?
Respuesta: No es necesario cerrar el canal si nadie espera su cierre a través de range. Pero si se utiliza el ciclo for v := range ch — sí, el canal debe cerrarse obligatoriamente, de lo contrario el ciclo no terminará.
2. ¿Qué sucederá si se lee de un canal cerrado?
Respuesta: Se devolverán valores hasta que se agote el búfer, luego, un valor cero y un indicador false. Se puede leer de forma segura hasta el final después de cerrar.
ch := make(chan int, 1) ch <- 42 close(ch) v, ok := <-ch // v = 42, ok = true v, ok = <-ch // v = 0, ok = false
3. ¿Quién debe cerrar el canal: el lector o el escritor?
Respuesta: Siempre el "escritor" (quien envía valores y sabe cuándo ha terminado). El "lector" no debe cerrar el canal para evitar condiciones de carrera.
En la aplicación, varias gorutinas escriben en un solo canal, una lee. Alguien cerró el canal en el lector, y otra gorutina intentó enviar — panic.
Ventajas: Lógica de interacción relativamente simple. Desventajas: Imposibilidad de predecir el momento de cierre, pánicos, condiciones de carrera.
El escritor lleva un registro de todas las gorutinas, utiliza sync.WaitGroup, y cierra el canal solo después de que todos los escritores han terminado. El lector termina correctamente el ciclo range después de cerrar el canal, maneja errores de forma segura.
Ventajas: Sincronización correcta, sin fugas ni deadlocks, comportamiento predecible. Desventajas: Lógica un poco más complicada, es necesario finalizar explícitamente todos los trabajos.