Pular para o conteúdo principal

victorstein.dev

36/100 Dias de Golang - Select Goroutines

Table of Contents

# Select Goroutines

Fiz vários posts falando sobre goroutines:

  1. 17/100 Dias de Golang - Concorrência e Goroutines
  2. 18/100 Dias de Golang - Goroutines, WaitGroup, Race condition e Mutex
  3. 19/100 Dias de Golang - Channels
  4. 20/100 Dias de Golang - Exemplo de goroutines, mutex e channels

Segundo o roadmap.sh do Golang, não fiz um post falando sobre Select, vamos corrigir isso então.

Se você já usou uma linguagem que possui switch, vai notar que select funciona de maneira semelhante, mas é voltado para a comunicação entre canais. O select permite que uma goroutine espere em múltiplos canais ao mesmo tempo. Ele escolhe um dos casos cujo canal está pronto para receber ou enviar e executa o bloco correspondente. Veja a sintaxe básica:

select {
case msg1 := <-ch1:
    fmt.Println("Recebido de ch1:", msg1)
case msg2 := <-ch2:
    fmt.Println("Recebido de ch2:", msg2)
default:
    fmt.Println("Nenhum canal pronto")
}

Veja esse exemplo mais completo:

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func produtor(nome string, ch chan<- string) {
	for {
		tempo := time.Duration(rand.Intn(1000)) * time.Millisecond
		time.Sleep(tempo)
		ch <- fmt.Sprintf("Mensagem de %s após %v", nome, tempo)
	}
}

func main() {

	ch1 := make(chan string)
	ch2 := make(chan string)

	go produtor("produtor1", ch1)
	go produtor("produtor2", ch2)

	for i := 0; i < 5; i++ {
		select {
		case msg := <-ch1:
			fmt.Println("Recebido:", msg)
		case msg := <-ch2:
			fmt.Println("Recebido:", msg)
		}
	}
}

Veja a saída:

Recebido: Mensagem de produtor2 após 425ms
Recebido: Mensagem de produtor2 após 63ms
Recebido: Mensagem de produtor1 após 858ms
Recebido: Mensagem de produtor1 após 306ms
Recebido: Mensagem de produtor2 após 947ms

A função produtor executa em um loop infinito, gerando mensagens personalizadas com o nome do produtor e o tempo de espera, que varia aleatoriamente entre 0 e 1000 milissegundos.

func produtor(nome string, ch chan<- string) {
	for {
		tempo := time.Duration(rand.Intn(1000)) * time.Millisecond
		time.Sleep(tempo)
		ch <- fmt.Sprintf("Mensagem de %s após %v", nome, tempo)
	}
}

Na função main, são criados dois canais (ch1 e ch2) e iniciadas duas goroutines produtoras em paralelo.

ch1 := make(chan string)
ch2 := make(chan string)

go produtor("produtor1", ch1)
go produtor("produtor2", ch2)

Usamos o select para escutar ambos os canais simultaneamente, imprimindo as primeiras 5 mensagens recebidas, independentemente de qual produtor as enviou.

for i := 0; i < 5; i++ {
		select {
		case msg := <-ch1:
			fmt.Println("Recebido:", msg)
		case msg := <-ch2:
			fmt.Println("Recebido:", msg)
		}
	}

Podemos também usar o select para definir um tempo limite de espera, esperar um request de uma API externa por no máximo 3 segundos e caso demore mais que isso vou tratar esse “erro”.

select {
case resp := <-responseChan:
    // processa resposta
case <-time.After(3 * time.Second):
    // timeout
}