Uma dúvida que assombra a maioria dos desenvolvedores Go, e não exclusivamente iniciantes, é sobre quando utilizar goroutines.
Nesse post vou dar algumas dicas para ajudar na análise e tomada de decisão na hora de adotar ou não a utilização de goroutines em seu projeto.
Antes de mais nada, assim como qualquer coisa relacionada a tecnologia, nem todo projeto faz sentido utilizar goroutines. Você pode até pensar, “meu sistema está lento. Já sei, vou usar goroutines para resolver.”, gastar muito tempo, já que SÓ colocar um go antes da chamada da função pode não ser o suficiente, e no final não ver melhora nenhuma ou até mesmo notar uma piora no desempenho.
Por isso, se você está pensando em adotar goroutines para melhorar o desempenho do seu sistema, eu sugiro que antes disso, faça um benchmark + profile do seu código para tentar identificar o que pode ser melhorado.
Se o seu caso não é esse, antes de adicionar goroutines, é bom analisar as funções que você utiliza em seu código e se elas não fazem uso de goroutines, como por exemplo o tratamento de requests HTTP.
A implementação do package net/http abre uma nova goroutine para cada request recebida. Sendo assim, não faz sentido que a cada request recebida você abra outra goroutine para lidar com a request.
Agora, se você já fez um benchmark + profile e entendeu o que as funções ao redor do seu código fazem, e você precisa/pode ter o processamento de tarefas de forma concorrente, aí sim chegou a hora de realmente pensar em goroutines.
Pensar em goroutines é mudar o pensamento comum e sequencial para um pensamento concorrente, ou seja, onde multiplas tarefas acontecerão ao mesmo tempo.
Como exemplo, vamos imaginar um endpoint que recebe o cadastro de uma pessoa. Esse cadastro, além das informações de texto, recebe 3 imagens (foto da pessoa, cpf e rg). Essas imagens são salvas em buckets na cloud onde a aplicação está hospedada.
func upload(img []byte) {
// FAZ UPLOAD
}
func HandleAdd(w http.ResponseWriter, r *http.Request) {
// PARSE BODY
upload(p.Image)
upload(p.Cpf)
upload(p.Rg)
// continua
}
Nesse cenário, poderiamos adicionar uma goroutine para o upload de cada imagem. Porém, como disse anteriormente, esse caso não seria um simples go func, pois é necessário que nossa goroutine principal saiba se houve algum erro ao tentar fazer o upload de qualquer uma das imagens.
Para conseguir tratar a resposta de cada um dos uploads, podemos ter um channel de tamanho 3, onde cada goroutine de upload vai escrever um erro caso não tenha conseguido fazer o upload da imagem.
É importante também que tenhamos criado um *sync.WaitGroup na goroutine principal para gerir a execução das goroutines de upload.
No final, para que o “é só adicionar um go na frente da chamada da função” funcionasse de forma correta, teríamos alterado nosso código para algo parecido com isso:
func upload(img []byte, cerr chan<- error, wg *sync.WaitGroup) {
// FAZ UPLOAD
cerr <- err
wg.Done()
}
func HandleAdd(w http.ResponseWriter, r *http.Request) {
// PARSE BODY
var (
errs := make(chan error, 3)
wg sync.WaitGroup
)
wg.Add(3)
go upload(p.Image, errs, wg)
go upload(p.Cpf, errs, wg)
go upload(p.Rg, errs, wg)
wg.Wait()
close(errs)
for err := errs {
if err != nil {
// trata erro
}
}
// continua
}
Goroutines são maravilhosas e podem trazer muitos beneficios para uma aplicação Go, porém é importante que sejam usadas com cuidado.
Meu conselho final para saber se convém adicionar uma goroutine à sua aplicação, é pensar se aquela tarefa ou conjunto de tarefas pode ser executada de forma concorrente e sem aumentar incrívelmente a complexidade para manutenção do código.
Ahh.. E nunca se esqueça de criar mecanismos para validação e controle das goroutines, para que elas não “saiam de controle” e acabem prejudicando sua aplicação.
Deixem suas dúvidas nos comentários.
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.

