Lección 12 · 12 de 20
Canales · Go
Comunique goroutines en Go con canales: enviar y recibir, cerrar un canal, recorrerlo con range y montar una tubería de dos etapas.

Un canal es un tubo con tipo por el que las goroutines se pasan valores. make(chan Line) crea uno de líneas de pedido; out <- l envía y <-total recibe. Es la forma que propone Go de coordinar: «no comunique compartiendo memoria; comparta memoria comunicando».
Este canal no tiene búfer: cada envío espera a que alguien reciba, y cada recepción espera a que alguien envíe. Así el canal, además de pasar datos, sincroniza: produce no puede adelantarse a quien lee.
produce es un patrón habitual, el generador: crea el canal, lanza una goroutine que lo llena y lo devuelve enseguida. El tipo de retorno, <-chan Line, es un canal solo de recepción: quien lo recibe no puede enviar ni cerrar. Al terminar, defer close(out) lo cierra para avisar de que no vendrán más valores.
La segunda etapa recorre el canal con for l := range produce(lines), que recibe hasta que el canal se cierra; sin el close, el bucle esperaría para siempre. Escribe cada línea, acumula el importe y envía el total por otro canal. En main, <-total se queda esperando hasta que llega ese valor, sin necesidad de un WaitGroup.
Con un solo emisor y un solo receptor, el orden de los valores se conserva y la salida es siempre la misma. Dos reglas: cierra el canal quien envía, nunca quien recibe, y enviar a un canal cerrado provoca un panic.
Código · main.go
package mainimport "fmt"type Line struct { Item string Qty int Price float64}func produce(lines []Line) <-chan Line { // sends every line, then closes the channel out := make(chan Line) // unbuffered: each send waits for a receiver go func() { defer close(out) for _, l := range lines { out <- l } }() return out}func main() { lines := []Line{{"Coffee", 2, 3.5}, {"Croissant", 3, 1.8}, {"Orange juice", 1, 2.9}} total := make(chan float64) go func() { // a second stage: receives the lines, sends back one total sum := 0.0 for l := range produce(lines) { // the loop ends when produce closes its channel fmt.Printf("%-13s %d x %.2f\n", l.Item, l.Qty, l.Price) sum += float64(l.Qty) * l.Price } total <- sum }() fmt.Printf("total: %.2f EUR\n", <-total) // main waits here until the total arrives}Salida · go run main.go
Coffee 2 x 3.50 Croissant 3 x 1.80 Orange juice 1 x 2.90 total: 15.30 EUR
Para recordar
ch <- venvía;v := <-chrecibe.- Sin búfer, cada envío espera a su receptor: el canal sincroniza.
<-chan Tes un canal solo de recepción.close(ch)avisa de que no hay más valores;rangetermina con él.- Cierra el que envía; enviar a un canal cerrado es un panic.
Ejercicio
Añada una tercera etapa entre las dos: una función discount(in <-chan Line) <-chan Line que rebaje un 10 % el precio de las líneas con Qty de 3 o más, y encadénela con range discount(produce(lines)).