Menu
Coddy logo textTech

Instrução Select

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 62 de 107.

A instrução select permite que uma goroutine espere por múltiplas operações de canal simultaneamente. É como uma instrução switch, mas cada case envolve um envio ou recebimento de canal. Quando múltiplos canais estão prontos, o select escolhe um aleatoriamente.

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)
    }
}

Sem o select, receber de ch1 bloquearia até que os dados chegassem, potencialmente perdendo dados em ch2. Com o select, o canal que receber dados primeiro será processado.

O caso default torna o select não bloqueante — ele executa imediatamente se nenhum canal estiver pronto:

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

Um padrão comum combina select com timeouts usando time.After, que retorna um canal que recebe um valor após a duração especificada:

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

Isso evita que seu programa espere indefinidamente quando uma operação de canal pode nunca ser concluída.

challenge icon

Desafio

Fácil

Vamos construir um monitor de serviço que utiliza a instrução select para lidar com múltiplas fontes de dados concorrentes. Seu monitor ouvirá diferentes canais de serviço simultaneamente e responderá apropriadamente quando os dados chegarem ou quando não houver atualizações disponíveis.

Você organizará seu código em dois arquivos:

  • monitor.go: Defina sua lógica de monitoramento de serviço com manipulação de canais baseada em select.

    Crie uma struct ServiceUpdate com os campos Name (string) e Status (string).

    Implemente uma função Monitor que recebe três canais: primary chan ServiceUpdate, backup chan ServiceUpdate e done chan bool. Esta função deve usar select para ouvir os três canais simultaneamente:

    • Ao receber de primary: retorne a string formatada "Primary: [Name] is [Status]"
    • Ao receber de backup: retorne a string formatada "Backup: [Name] is [Status]"
    • Ao receber de done: retorne "Monitoring stopped"

    Implemente uma função CheckStatus que recebe um único updates chan ServiceUpdate e usa select com um caso default para realizar uma verificação não bloqueante:

    • Se uma atualização estiver disponível: retorne "Update available: [Name]"
    • Se nenhuma atualização estiver disponível (caso default): retorne "No updates pending"
  • main.go: Configure os canais e demonstre o comportamento do select bloqueante e não bloqueante.

    Leia um modo da entrada que determina qual cenário executar: "primary", "backup", "stop" ou "check".

    Para os modos "primary", "backup" e "stop": leia também um nome de serviço e um status. Crie os três canais, inicie uma goroutine que envia dados para o canal apropriado com base no modo, então chame Monitor e imprima seu resultado.

    Para o modo "check": leia um nome de serviço e um status, além de uma flag ("send" ou "nosend") indicando se deve enviar dados antes de verificar. Crie um canal com buffer de capacidade 1. Se a flag for "send", envie uma atualização para o canal antes de chamar CheckStatus. Imprima o resultado de CheckStatus.

As seguintes entradas serão fornecidas:

  • Linha 1: Modo ("primary", "backup", "stop" ou "check")
  • Linha 2: Nome do serviço
  • Linha 3: Status do serviço
  • Linha 4 (apenas para o modo "check"): "send" ou "nosend"

Por exemplo, dado:

primary
Database
healthy

Sua saída deve ser:

Primary: Database is healthy

E dado:

check
Cache
active
nosend

Sua saída deve ser:

No updates pending

Folha de consulta

A instrução select permite que uma goroutine aguarde múltiplas operações de canal simultaneamente. Ela funciona como uma instrução switch, mas cada caso envolve um envio ou recebimento de canal:

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

Quando múltiplos canais estão prontos, o select escolhe um aleatoriamente. Sem o select, receber de um canal bloquearia e potencialmente perderia dados em outro canal.

O caso default torna o select não bloqueante — ele executa imediatamente se nenhum canal estiver pronto:

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

Um padrão comum combina o select com timeouts usando time.After, que retorna um canal que recebe um valor após a duração especificada:

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

Isso evita que seu programa espere para sempre quando uma operação de canal pode nunca ser concluída.

Experimente você mesmo

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: Lidar com diferentes modos: "primary", "backup", "stop", "check"
	
	// Para os modos "primary", "backup", "stop":
	// - Criar três canais: primary, backup, done
	// - Iniciar uma goroutine que envia dados para o canal apropriado
	// - Chamar Monitor e imprimir o resultado
	
	// Para o modo "check":
	// - Ler a flag adicional ("send" ou "nosend")
	// - Criar um canal com buffer de capacidade 1
	// - Se a flag for "send", enviar uma atualização para o canal
	// - Chamar CheckStatus e imprimir o resultado
	
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos