Programação Assíncrona em Go: guia completo com exemplos

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

Introdução: Por que a programação assíncrona em Go?



Go (Golang) foi projetado desde o início para trabalhar eficientemente com concorrência. Ao contrário de muitas linguagens onde a assincronicidade é implementada através de callbacks complexos ou promises, Go oferece um modelo elegante baseado em goroutines e channels. Este modelo permite escrever código assíncrono que é lido como síncrono, mas executado paralelamente, maximizando a utilização dos recursos de processadores multi-core.



Neste guia, abordaremos todos os conceitos-chave da programação assíncrona em Go: desde a criação de goroutines até padrões complexos de sincronização usando channels e primitivas do pacote sync. O material será útil tanto para iniciantes que estão aprendendo Go quanto para desenvolvedores experientes que desejam sistematizar seus conhecimentos.



1. Goroutines: Threads leves



Uma goroutine é uma thread de execução leve, gerenciada pelo runtime do Go. Ao contrário das threads do SO, as goroutines têm uma pilha minúscula (começando com alguns KB) que pode crescer e encolher dinamicamente. Isso permite executar dezenas de milhares de goroutines simultaneamente sem consumo significativo de memória.



Iniciando uma goroutine

Para executar uma função como goroutine, basta adicionar a palavra-chave go antes da chamada da função:



package main

import ( "fmt" "time")

func printNumbers() { for i := 1; i <= 5; i++ { fmt.Println(i) time.Sleep(100 * time.Millisecond) }}

func main() { // Executa a função como uma goroutine go printNumbers()

// A goroutine principal continua trabalhando fmt.Println("Goroutine principal está trabalhando")

// Dá tempo para a goroutine filha executar time.Sleep(1 * time.Second) fmt.Println("Programa finalizado")}


Importante: O programa termina quando a função main() termina. Se a goroutine principal terminar antes, todas as goroutines filhas serão interrompidas forçadamente. Por isso, no exemplo, usamos time.Sleep para esperar — na prática, para sincronização, usam-se channels ou sync.WaitGroup.



Funções anônimas em goroutines

Goroutines são frequentemente criadas com funções anônimas:



go func() {    fmt.Println("Executando em uma goroutine")}()


2. Channels: Comunicação entre goroutines



Channels são "tubos" tipados através dos quais as goroutines podem enviar e receber valores. Este é o principal mecanismo de sincronização e troca de dados em Go. Os channels garantem acesso seguro aos dados sem o uso de locks explícitos (mutexes).



Criação e uso de um channel

Um channel é criado com a função make(). O operador <- é usado para enviar e receber dados:



package main

import "fmt"

func sum(s []int, c chan int) { total := 0 for _, v := range s { total += v } c <- total // envia o resultado para o channel}

func main() { s := []int{7, 2, 8, -9, 4, 0}

c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c)

x, y := <-c, <-c // recebe dados do channel

fmt.Println(x, y, x+y)}


Neste exemplo, iniciamos duas goroutines, cada uma calcula a soma de metade do slice. Os resultados são enviados para o channel, e a goroutine principal os recebe e exibe a soma total.



Channels com buffer

Por padrão, os channels não têm buffer — o envio é bloqueado até que o receptor esteja pronto para receber o valor. Channels com buffer têm uma fila de tamanho definido:



ch := make(chan int, 2) // channel com buffer para 2 elementosch <- 1ch <- 2// ch <- 3 // erro: deadlock, buffer cheiofmt.Println(<-ch) // 1fmt.Println(<-ch) // 2

Blogs

Book Recommendations