Lección 13 · 13 de 20
Canales con búfer y select · Go
Use canales con búfer como colas en Go, envíe sin bloquear con select y default, y ponga un tiempo límite con select y time.After.

make(chan string, 3) crea un canal con búfer: admite hasta tres mensajes sin que nadie los reciba todavía. Funciona como una cola de correos pendientes. len(queue) dice cuántos hay dentro y cap(queue), cuántos caben.
`select` espera a la vez en varias operaciones de canal y ejecuta la primera que pueda hacerse. Con un case queue <- msg y un `default`, el envío deja de bloquear: si hay sitio, se encola; si el búfer está lleno, se ejecuta default y el mensaje se descarta. Por eso newsletter y survey no entran.
Después de close(queue) ya no se puede enviar, pero los mensajes que hay en el búfer siguen ahí: range los entrega todos, en orden, y termina cuando la cola queda vacía.
La segunda parte pone un tiempo límite. Una goroutine tarda 300 ms en preparar un informe; time.After(50 * time.Millisecond) devuelve un canal que recibe un valor pasados 50 ms. El select se queda con lo que llegue antes: el plazo. El canal report tiene búfer de 1 para que la goroutine pueda dejar su resultado aunque ya nadie lo espere y no se quede bloqueada para siempre.
Si varios case están listos a la vez, select elige uno al azar. Este ejemplo lo evita a propósito: o solo hay un caso posible, o los tiempos están muy separados (50 frente a 300 ms), y por eso la salida es siempre la misma.
Código · main.go
package mainimport ( "fmt" "time")func main() { queue := make(chan string, 3) // buffered: holds 3 messages without a receiver for _, msg := range []string{"welcome email", "invoice 17", "password reset", "newsletter", "survey"} { select { case queue <- msg: fmt.Printf("queued %-15s len=%d cap=%d\n", msg, len(queue), cap(queue)) default: // the buffer is full: do not block, drop the message fmt.Println("full, dropped", msg) } } close(queue) for msg := range queue { // the buffered values are still delivered after close fmt.Println("sending", msg) } report := make(chan string, 1) // buffer 1: the sender never gets stuck go func() { time.Sleep(300 * time.Millisecond) // a slow job report <- "sales report ready" }() select { // waits for whichever case is ready first case r := <-report: fmt.Println(r) case <-time.After(50 * time.Millisecond): fmt.Println("timeout: report not ready after 50ms") }}Salida · go run main.go
queued welcome email len=1 cap=3 queued invoice 17 len=2 cap=3 queued password reset len=3 cap=3 full, dropped newsletter full, dropped survey sending welcome email sending invoice 17 sending password reset timeout: report not ready after 50ms
Para recordar
make(chan T, n)crea un canal con búfer denvalores.selectejecuta la primera operación de canal que esté lista.- Con
default,selectno bloquea nunca. - Tras
close,rangetodavía entrega lo que queda en el búfer. time.Afteren unselectpone un tiempo límite.
Ejercicio
Cambie la capacidad de queue a 5 y compruebe que ya no se descarta nada. Después cambie el plazo a 500 * time.Millisecond y vea cómo ahora gana el informe.