Saltar al contenido principal
eLearner.app
Módulo 7 · Lección 3 de 533/50 en el curso~12 min
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

Go
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 select se 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

Go
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

Go
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:

Go
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

Go
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:

Go
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

Ejercicio#go.m7.l3.e1
Intentos: 0Cargando...

Implementar un select con dos casos que reciben de ch1 y ch2; imprime cuál llegó primero.

Cargando editor...
Mostrar pista

Sintaxis `select { case ...: ... }` con un caso por canal.

Solución disponible después de 3 intentos

Ejercicio#go.m7.l3.e2
Intentos: 0Cargando...

Agregue un caso de tiempo de espera con time.After(time.Second) a la selección.

Cargando editor...
Mostrar pista

`time.After(d)` es un canal que recibe después de d.

Solución disponible después de 3 intentos

Cuestionario#go.m7.l3.e3
Listo

¿Qué hace que un selecto no sea bloqueante?

Go
select { case v := <-ch: ... ??? }
Opciones de respuesta

Resumen

  • select espera 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: use context).
  • Patrón: canal terminado, bucle de eventos, canal nulo para desactivar un caso.
  • select{} vacío = bloqueo eterno: casi siempre es un error.