Desafio Go

Pipeline concorrente com detecção de corrida

Enunciado

Implemente um pipeline com três estágios: (1) gerar números de 1 a 10 em um canal; (2) dobrar cada número em goroutines separadas; (3) somar os resultados. Use sync.WaitGroup para sincronizar e go run -race para verificar ausência de data races. O resultado final deve ser a soma dos dobros (110).

Requisitos

  • Criar três funções: gerar, dobrar e somar.
  • Usar canais para comunicação entre estágios.
  • Usar sync.WaitGroup para esperar as goroutines de dobra.
  • O programa deve rodar sem avisos com go run -race.
  • Imprimir a soma final igual a 110.

Código inicial

package main

import (
	"fmt"
)

func main() {
	// TODO: implementar pipeline com canais, goroutines e WaitGroup
}

Saída esperada

Soma: 110
Ver dica

Use um canal de entrada, um canal de saída e um WaitGroup para as goroutines que dobram. Feche o canal de saída após o Wait.

Mostrar solução
package main

import (
	"fmt"
	"sync"
)

func gerar(nums []int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for _, n := range nums {
			out <- n
		}
	}()
	return out
}

func dobrar(in <-chan int) <-chan int {
	out := make(chan int)
	var wg sync.WaitGroup
	for i := 0; i < 3; i++ { // 3 workers
		wg.Add(1)
		go func() {
			defer wg.Done()
			for n := range in {
				out <- n * 2
			}
		}()
	}
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

func somar(in <-chan int) int {
	total := 0
	for n := range in {
		total += n
	}
	return total
}

func main() {
	nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	c1 := gerar(nums)
	c2 := dobrar(c1)
	total := somar(c2)
	fmt.Println("Soma:", total)
}

Passo a passo

  1. gerar cria um canal out, inicia uma goroutine que envia os números e fecha o canal ao terminar.
  2. dobrar cria um canal out e um WaitGroup.
  3. Iniciamos 3 workers (goroutines) que leem de in, dobram o valor e enviam para out.
  4. Cada worker usa defer wg.Done() para decrementar o contador ao final.
  5. Uma goroutine separada espera o WaitGroup e fecha out após todos os workers terminarem.
  6. somar consome out até o canal ser fechado, acumulando o total.
  7. A main chama os estágios em sequência e imprime o resultado.

Por que funciona

O pipeline usa canais para passar dados entre estágios, evitando compartilhamento direto de memória. O WaitGroup coordena os workers de dobra: só fecha o canal de saída quando todos terminam. Como não há acesso concorrente a variáveis compartilhadas sem sincronização, o -race não detecta problemas. A soma é feita sequencialmente no estágio final, garantindo o resultado correto.

Erros comuns

  • Esquecer de fechar o canal de saída: somar fica bloqueado para sempre (deadlock). Corrija com close(out) após wg.Wait().
  • Fechar o canal antes de todos os workers terminarem: panic por envio em canal fechado. Use a goroutine que espera o WaitGroup.
  • Não usar WaitGroup e fechar o canal imediatamente: perda de dados. Sincronize corretamente.

Outra forma de resolver

Usar sync/atomic para a soma, mas exigiria compartilhar a variável entre goroutines. A abordagem com canais é mais idiomática e segura.

Saída esperada

Soma: 110