Programação Assíncrona em Go: Guia Completo para Iniciantes

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Programação Assíncrona em Go: Guia Completo para Iniciantes

Go (Golang) foi criado pensando nas necessidades do desenvolvimento moderno, onde desempenho e concorrência desempenham papéis-chave. Ao contrário de muitas linguagens, onde a assincronia é implementada através de callbacks ou promises, Go oferece mecanismos integrados e elegantes: goroutines e canais. Este guia apresentará os fundamentos da programação assíncrona em Go e mostrará como escrever código concorrente eficiente.



O que são goroutines e como elas diferem das threads?

Goroutine é uma thread de execução leve, gerenciada pelo runtime do Go. Ao contrário das threads do sistema operacional, as goroutines consomem muito menos recursos: sua pilha começa com alguns kilobytes e pode crescer e diminuir dinamicamente. Isso permite executar milhões de goroutines simultaneamente sem sobrecarga significativa de memória.

Iniciar uma goroutine é muito simples — basta adicionar a palavra-chave go antes da chamada de função:

package main

import ( "fmt" "time")

func sayHello() { fmt.Println("Olá da goroutine!")}

func main() { go sayHello() // Iniciando a goroutine time.Sleep(100 * time.Millisecond) // Aguardamos para a goroutine ter tempo de executar fmt.Println("Função principal finalizada")}

É importante notar: se a função principal main() for finalizada, todas as goroutines em execução serão interrompidas forçadamente. Para sincronização, usamos ferramentas especiais.



Sincronização com WaitGroup

A maneira mais simples de aguardar a conclusão das goroutines é usar sync.WaitGroup. Ele funciona como um contador: incrementamos ao iniciar uma goroutine e decrementamos ao finalizar. A função principal fica bloqueada até que o contador chegue a zero.

package main

import ( "fmt" "sync" "time")

func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // Decrementa o contador ao finalizar fmt.Printf("Trabalhador %d começou o trabalho", id) time.Sleep(time.Second) fmt.Printf("Trabalhador %d terminou o trabalho", id)}

func main() { var wg sync.WaitGroup

for i := 1; i <= 3; i++ { wg.Add(1) // Incrementa o contador antes de iniciar go worker(i, &wg) }

wg.Wait() // Aguarda a conclusão de todas as goroutines fmt.Println("Todos os trabalhadores terminaram o trabalho")}

Observe: wg.Add(1) é chamado antes de iniciar a goroutine, e wg.Done() é chamado dentro dela (geralmente via defer). Isso garante que o contador seja decrementado corretamente mesmo em caso de pânico.



Canais: comunicação entre goroutines

Canais (channels) são "tubos" tipados através dos quais as goroutines podem trocar dados com segurança. Os canais fornecem sincronização por padrão: o envio para um canal bloqueia a goroutine até que outra goroutine leia o valor, e vice-versa.

Os canais são criados com a função make:

package main

import "fmt"

func main() { // Criando um canal não bufferizado para inteiros ch := make(chan int)

// Iniciando uma goroutine que envia dados go func() { ch <- 42 // Envia o valor para o canal }()

// Lendo do canal (bloqueia até que um valor chegue) value := <-ch fmt.Println("Valor recebido:", value)}

Os canais podem ser:

  • Não bufferizados — síncronos, o envio bloqueia até o recebimento.
  • Bufferizados — assíncronos, o envio bloqueia apenas quando o buffer está cheio.
// Criando um canal bufferizado para 3 elementosbufferedCh := make(chan string, 3)bufferedCh <- "A"bufferedCh <- "B"bufferedCh <- "C" // Enquanto não estiver cheio — não bloqueia


Blogs

Book Recommendations