Goroutines e channels são fundamentais para a programação concorrente em Go, proporcionando uma maneira eficiente de realizar tarefas simultâneas. Caso você ainda não esteja tão familiarizado com goroutines e channels recomendo a leitura:
- O que são e como funcionam as Goroutines
- Aguardando execução de múltiplas goroutines
- Como acessar uma variável entre goroutines com package sync/atomic
- Diferença entre Goroutines e Threads
- O que são e como utilizar channels
- Buffer de mensagens em channels
- Múltiplos channels e a cláusula select
- Com resolver race condition com Mutex e Channel
- Playlist Concorrência em Go
No entanto, embora a facilidade para se lidar com programação concorrente em Go seja grande, para garantir que o código seja eficiente e livre de erros, é essencial prestar atenção a alguns pontos.
1. Sincronização de Goroutines
Goroutines são leves e eficientes, mas sem uma sincronização adequada, elas podem levar a race conditions, ou seja, múltiplas goroutines acessam e modificam dados compartilhados simultaneamente. Utilize mecanismos como sync.Mutex ou sync.WaitGroup para garantir que as goroutines não entrem em conflito ao acessar dados compartilhados.
Exemplo com sync.WaitGroup:
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Println(i)
}(i)
}
wg.Wait()
2. Uso Adequado de Channels
Channels são utilizados para comunicação segura entre goroutines. No entanto, é crucial fechar os channels corretamente para evitar deadlocks e panics.
Em outras palavras, feche os channels somente quando todas as goroutines terminarem de enviar dados e nunca envie dados para um channel fechado, pois isso causará um panic.
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}()
for val := range ch {
fmt.Println(val)
}
3. Evitando Deadlocks
Deadlocks ocorrem quando goroutines ficam bloqueadas indefinidamente esperando por operações que nunca ocorrerão. Para evitar deadlocks, certifique-se de que para cada operação de recebimento de dados (<-ch), existe uma operação de envio (ch <-) correspondente, e vice-versa.
Exemplo de potencial deadlock:
ch := make(chan int) ch <- 1 // Goroutine principal bloqueada aqui, esperando que alguém receba o valor. fmt.Println(<-ch) // Nunca alcançado.
Correção:
ch := make(chan int)
go func() {
ch <- 1
}()
fmt.Println(<-ch)
4. Buffered Channels
Channels podem ser bufferizados ou não. Channels bufferizados permitem que você envie múltiplos valores antes que qualquer goroutine esteja pronta para recebê-los. Utilize buffered channels para reduzir a sobrecarga de sincronização e evitar bloqueios frequentes.
ch := make(chan int, 2)
go func() {
ch <- 1
ch <- 2
}
fmt.Println(<-ch)
fmt.Println(<-ch)
5. Evitando Variações Não Determinísticas
A execução concorrente pode levar a resultados não determinísticos. Portanto, testar e depurar código concorrente pode ser uma tarefa bem desafiadora.
Para ajudar na detecção de race conditions e outras questões relacionadas a concorrência, adicione a flag -race a execução dos seus testes (go test -race).
6. Limpeza Adequada
Goroutines que não são finalizadas corretamente podem levar a “vazamentos”, consumindo memória e outros recursos desnecessariamente. Utilize select com um canal de cancelamento para permitir que goroutines sejam finalizadas quando não são mais necessárias.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
// Realiza o trabalho da goroutine.
}
}
}(ctx)
Conclusão
Utilizar goroutines e channels de maneira eficiente e segura é fundamental para tirar o máximo proveito da concorrência em Go.
Para garantir que seu código seja robusto e livre de erros, preste sempre atenção aos pontos de sincronização, fechamento de channels, prevenção de deadlocks, uso de channels bufferizados, variações não determinísticas e limpeza adequada de goroutines.
Com essas práticas, você poderá desenvolver aplicações concorrentes poderosas e eficientes em Go.
Outra coisa que também pode te ajudar muito a desenvolver aplicações poderosas é fazer um dos nossos cursos ou imersões.
Até a próxima!
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.
Livros Recomendados
Abaixo listei alguns dos melhores livros que já li sobre GO.




