Lecciones del módulo (3/5)
select: multiplexación de canales
select espera en operaciones de múltiples canales al mismo tiempo.
Es el switch de la competición Go: te permite reaccionar al primero
canal listo entre muchos.
Sintaxis
select {
case v := <-ch1:
fmt.Println("ch1:", v)
case v := <-ch2:
fmt.Println("ch2:", v)
case ch3 <- 99:
fmt.Println("inviato su ch3")
case <-time.After(time.Second):
fmt.Println("timeout")
default:
fmt.Println("nessuno è pronto")
}Semántica:
- Todas las viviendas son evaluadas; sólo se elige uno de los casos "listos".
- Si hay varias casas listas, la elección es pseudoaleatoria (sin orden).
- Si ningún caso está listo: el
selectse bloquea hasta que uno esté listo. - Si hay un
default: se ejecuta inmediatamente cuando no hay ningún caso listo (seleccione sin bloqueo).
Tiempo de espera del patrón
select {
case res := <-fetch(url):
use(res)
case <-time.After(2 * time.Second):
log.Println("timeout: fetch troppo lento")
}time.After(d) devuelve un <-chan Time que produce un valor después de d.
Es la forma clásica de limitar la espera.
Seleccione no bloqueo por defecto
select {
case msg := <-ch:
process(msg)
default:
// niente da fare, prosegui
}"Encuesta" el canal libremente. Útil en colas que deben permanecer reactivo sin tener que esperar.
Patrón "canal terminado"
Combine select con un canal de borrado:
func worker(in <-chan Job, done <-chan struct{}) {
for {
select {
case j := <-in:
process(j)
case <-done:
return // chi gestisce done segnala lo shutdown
}
}
}close(done) por el coordinador activa el case <-done
en todas las gorutinas pendientes: apagado coordinado.
Bucle con selección
for {
select {
case v := <-input:
if v == nil {
return
}
handle(v)
case <-time.After(5 * time.Second):
keepAlive()
case <-ctx.Done():
return
}
}El patrón de "bucle de eventos" típico de servidores y trabajadores.
Canal nulo porque las casas están "apagadas"
Un truco avanzado: un case <-ch con ch == nil nunca viene
seleccionado. Le permite "deshabilitar" dinámicamente un caso:
var in chan int = source()
for {
select {
case v, ok := <-in:
if !ok {
in = nil // disabilita questo case
continue
}
handle(v)
case <-ctx.Done():
return
}
}Cuando se cierra in, lo cancelo: el select continúa manejando
Sólo las otras casas.
Pruébalo tú mismo
Implementar un select con dos casos que reciben de ch1 y ch2; imprime cuál llegó primero.
Mostrar pista
Sintaxis `select { case ...: ... }` con un caso por canal.
Solución disponible después de 3 intentos
Agregue un caso de tiempo de espera con time.After(time.Second) a la selección.
Mostrar pista
`time.After(d)` es un canal que recibe después de d.
Solución disponible después de 3 intentos
¿Qué hace que un selecto no sea bloqueante?
select { case v := <-ch: ... ??? }Resumen
selectespera el primer caso listo entre operaciones de múltiples canales.- Elección pseudoaleatoria entre múltiples casas listas.
default→ seleccione sin bloqueo (sondeo).time.After(d)para tiempo de espera (cuidado con las fugas de bucle: usecontext).- Patrón: canal terminado, bucle de eventos, canal nulo para desactivar un caso.
select{}vacío = bloqueo eterno: casi siempre es un error.