Introdução: a pergunta que atormenta todo segundo iniciante em Go
Imagine: você está escrevendo seu primeiro programa sério em Go. Você aprendeu sobre goroutines, dominou os canais e até escreveu alguns exemplos funcionais. Mas de repente você encontra no Stack Overflow uma pergunta que acumulou milhares de visualizações: "Is it OK to leave a channel open?" (É permitido deixar um canal aberto?).
Spoiler: a resposta não é tão direta quanto gostaríamos. Alguns gritam que um canal não fechado é um pecado mortal e um vazamento de recursos. Outros dizem que o coletor de lixo do Go limpará tudo. Onde está a verdade? Vamos analisar com exemplos reais, sem enrolação e palestras acadêmicas.
Para quem está conhecendo a linguagem agora — Go (ou Golang) oferece um mecanismo elegante de sincronização através de canais. Mas como qualquer ferramenta poderosa, os canais exigem manuseio cuidadoso. Especialmente quando se trata de fechá-los.
Por que essa pergunta surgiu? A raiz do problema
Em Go, existe um ditado: "Não feche o canal pelo lado do receptor — feche pelo lado do remetente". Mas o que fazer se o programa terminar e o canal permanecer aberto? Vazamento de goroutines — esse é o maior medo. Quando uma goroutine "fica pendurada" esperando dados de um canal que ninguém nunca fechará, ela não termina. E goroutines não finalizadas significam vazamento de memória e produção comprometida.
Mas sejamos honestos: em scripts pequenos ou utilitários de teste, isso não é grave. O coletor de lixo do Go é inteligente, mas ele não limpa goroutines — apenas a memória que elas ocupam. Portanto, a questão "fechar ou não" depende da arquitetura da sua aplicação.
Exemplo 1: quando NÃO fechar o canal é normal
Considere um cenário simples: o programa lê dados, processa e termina. Se o canal é usado apenas dentro do main, e você tem certeza de que todas as goroutines terminarão — pode não fechá-lo.
package main
import "fmt"
func main() { ch := make(chan int)
// Goroutine remetente go func() { for i := 0; i < 5; i++ { ch <- i } // Canal NÃO é fechado }()
// Receptor for i := 0; i < 5; i++ { fmt.Println(<-ch) } // O programa terminará sem erros fmt.Println("Pronto, o canal ficou aberto, mas está tudo bem")}Por que isso funciona? Porque o main termina, e todas as goroutines são destruídas junto com o processo. O coletor de lixo do Go limpará a memória. Não há vazamento de goroutines — o programa simplesmente acabou.
Conclusão: se seu programa é um script que é executado e morre, deixar o canal aberto é absolutamente normal. Isso não causará vazamento se você controlar o ciclo de vida.
Exemplo 2: quando um canal não fechado é uma catástrofe
Agora imagine um servidor que funciona 24/7. Você tem uma goroutine que escuta o canal infinitamente:
package main
import ( "fmt" "time")
func worker(ch chan int) { // Leitura infinita for val := range ch { fmt.Println("Processado:", val) } // Nunca chegaremos aqui se o canal não for fechado fmt.Println("Goroutine finalizada")}
func main() { ch := make(chan int) go worker(ch)
// Enviando dados ch <- 1 ch <- 2 ch <- 3
// Esquecemos de fechar o canal // close(ch)
time.Sleep(2 * time.Second) fmt.Println("O programa está terminando, mas o worker ainda está pendurado!")}O que acontecerá? A goroutine worker ficará pendurada em um loop infinito, aguardando novos dados. Ela nunca será finalizada. Vazamento de goroutines evidente. Com o tempo, dezenas de milhares dessas goroutines se acumularão, e o servidor cairá com erro de memória.
Neste caso, o fechamento do canal é obrigatório. É o close(ch) que dá o sinal para a go