Menu
Coddy logo textTech

Sentencia Select

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 62 de 107.

La sentencia select permite que una goroutine espere en múltiples operaciones de canal simultáneamente. Es como una sentencia switch, pero cada caso involucra un envío o recepción de canal. Cuando múltiples canales están listos, select elige uno al azar.

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() { ch1 <- "from channel 1" }()
    go func() { ch2 <- "from channel 2" }()
    
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

Sin select, recibir de ch1 bloquearía hasta que lleguen los datos, perdiendo potencialmente datos en ch2. Con select, el canal que reciba datos primero será procesado.

El caso default hace que select sea no bloqueante; se ejecuta inmediatamente si ningún canal está listo:

select {
case msg := <-messages:
    fmt.Println("Received:", msg)
default:
    fmt.Println("No message available")
}

Un patrón común combina select con tiempos de espera usando time.After, el cual devuelve un canal que recibe un valor después de la duración especificada:

select {
case result := <-ch:
    fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
    fmt.Println("Timeout!")
}

Esto evita que tu programa espere indefinidamente cuando una operación de canal podría no completarse nunca.

challenge icon

Desafío

Fácil

Vamos a construir un monitor de servicios que utiliza la sentencia select para manejar múltiples fuentes de datos concurrentes. Tu monitor escuchará diferentes canales de servicio simultáneamente y responderá adecuadamente cuando lleguen datos o cuando no haya actualizaciones disponibles.

Organizarás tu código en dos archivos:

  • monitor.go: Define tu lógica de monitoreo de servicios con manejo de canales basado en select.

    Crea un struct ServiceUpdate con los campos Name (string) y Status (string).

    Implementa una función Monitor que reciba tres canales: primary chan ServiceUpdate, backup chan ServiceUpdate, y done chan bool. Esta función debe usar select para escuchar en los tres canales simultáneamente:

    • Al recibir de primary: devuelve el string formateado "Primary: [Name] is [Status]"
    • Al recibir de backup: devuelve el string formateado "Backup: [Name] is [Status]"
    • Al recibir de done: devuelve "Monitoring stopped"

    Implementa una función CheckStatus que reciba un único updates chan ServiceUpdate y use select con un caso default para realizar una comprobación no bloqueante:

    • Si hay una actualización disponible: devuelve "Update available: [Name]"
    • Si no hay ninguna actualización disponible (caso default): devuelve "No updates pending"
  • main.go: Configura los canales y demuestra el comportamiento de select tanto bloqueante como no bloqueante.

    Lee un modo de la entrada que determine qué escenario ejecutar: "primary", "backup", "stop", o "check".

    Para los modos "primary", "backup", y "stop": lee también un nombre de servicio y un estado. Crea los tres canales, lanza una goroutine que envíe datos al canal apropiado según el modo, luego llama a Monitor e imprime su resultado.

    Para el modo "check": lee un nombre de servicio y un estado, además de una bandera ("send" o "nosend") que indique si se deben enviar datos antes de la comprobación. Crea un canal con búfer de capacidad 1. Si la bandera es "send", envía una actualización al canal antes de llamar a CheckStatus. Imprime el resultado de CheckStatus.

Se proporcionarán las siguientes entradas:

  • Línea 1: Modo ("primary", "backup", "stop", o "check")
  • Línea 2: Nombre del servicio
  • Línea 3: Estado del servicio
  • Línea 4 (solo para el modo "check"): "send" o "nosend"

Por ejemplo, dado:

primary
Database
healthy

Tu salida debería ser:

Primary: Database is healthy

Y dado:

check
Cache
active
nosend

Tu salida debería ser:

No updates pending

Hoja de referencia

La sentencia select permite que una goroutine espere en múltiples operaciones de canal simultáneamente. Funciona de manera similar a una sentencia switch, pero cada caso involucra el envío o la recepción de un canal:

select {
case msg1 := <-ch1:
    fmt.Println(msg1)
case msg2 := <-ch2:
    fmt.Println(msg2)
}

Cuando múltiples canales están listos, select elige uno al azar. Sin select, recibir de un canal bloquearía la ejecución y potencialmente se perderían datos de otro canal.

El caso default hace que select sea no bloqueante: se ejecuta inmediatamente si ningún canal está listo:

select {
case msg := <-messages:
    fmt.Println("Received:", msg)
default:
    fmt.Println("No message available")
}

Un patrón común combina select con tiempos de espera (timeouts) usando time.After, que devuelve un canal que recibe un valor después de la duración especificada:

select {
case result := <-ch:
    fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
    fmt.Println("Timeout!")
}

Esto evita que su programa espere indefinidamente cuando una operación de canal podría no completarse nunca.

Pruébalo tú mismo

package main

import (
	"fmt"
)

func main() {
	var mode string
	fmt.Scanln(&mode)

	var serviceName string
	fmt.Scanln(&serviceName)

	var serviceStatus string
	fmt.Scanln(&serviceStatus)

	// TODO: Manejar diferentes modos: "primary", "backup", "stop", "check"
	
	// Para los modos "primary", "backup", "stop":
	// - Crear tres canales: primary, backup, done
	// - Lanzar una goroutine que envíe datos al canal apropiado
	// - Llamar a Monitor e imprimir el resultado
	
	// Para el modo "check":
	// - Leer el flag adicional ("send" o "nosend")
	// - Crear un canal con búfer con capacidad 1
	// - Si el flag es "send", enviar una actualización al canal
	// - Llamar a CheckStatus e imprimir el resultado
	
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos