Concorrência e Multithreading em Go: goroutines e canais

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 à concorrência em Go

A linguagem Go (Golang) foi desenvolvida no Google levando em consideração as demandas modernas de desempenho e escalabilidade. Uma das principais características do Go é o suporte embutido para concorrência (concurrency) — a capacidade de um programa executar várias tarefas simultaneamente. Ao contrário do multithreading tradicional, onde as threads são gerenciadas pelo sistema operacional, o Go oferece goroutines leves e primitivas de sincronização convenientes — os canais.

Neste artigo, abordaremos:

  • O que são goroutines e como executá-las
  • Canais para troca de dados entre goroutines
  • Mutexes para acesso seguro a recursos compartilhados
  • Exemplos práticos e padrões de código concorrente


1. Goroutines: threads leves do Go

Uma goroutine é uma função que é executada concorrentemente com outras goroutines. Ela ocupa apenas alguns kilobytes de pilha (ao contrário das threads do SO, onde a pilha pode ter megabytes). Para iniciar uma goroutine, basta adicionar a palavra-chave go antes da chamada da função.



Exemplo: Iniciando goroutines

package main

import ( "fmt" "time")

func sayHello(name string) { for i := 0; i < 3; i++ { fmt.Printf("Olá, %s! (iteração %d)", name, i) time.Sleep(100 * time.Millisecond) }}

func main() { go sayHello("Alice") // Iniciando goroutine go sayHello("Bob") // Mais uma goroutine time.Sleep(1 * time.Second) // Aguardando a conclusão fmt.Println("Função principal concluída")}

Importante: O programa termina quando a função main termina, mesmo que outras goroutines ainda estejam em execução. Por isso, usamos time.Sleep para aguardar. Em código real, é melhor usar sync.WaitGroup.



Usando sync.WaitGroup

package main

import ( "fmt" "sync")

func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // Decrementa o contador ao finalizar fmt.Printf("Trabalhador %d iniciou o trabalho", id) // ... algum trabalho ... fmt.Printf("Trabalhador %d finalizou o trabalho", id)}

func main() { var wg sync.WaitGroup

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

wg.Wait() // Bloqueia o main até todas as goroutines terminarem fmt.Println("Todos os trabalhadores finalizaram o trabalho")}


2. Canais: troca de dados entre goroutines

Canais (channels) são "tubos" tipados através dos quais goroutines podem enviar e receber valores. Os canais fornecem sincronização segura sem bloqueios explícitos.



Criação e uso de canais

package main

import "fmt"

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

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

// Recebendo valor do canal value := <-ch fmt.Println("Recebido:", value)}

Por padrão, os canais são não bufferizados: o envio é bloqueado até que o receptor esteja pronto para receber os dados, e vice-versa. Isso garante a sincronização.



Canais bufferizados

package main

import "fmt"

func main() { // Canal com buffer para 3 elementos ch := make(chan string, 3)

// Enviando sem bloqueio (enquanto o buffer não estiver cheio) ch <- "um" ch <- "dois" ch <- "três"

// Lendo fmt.Println(<-ch) fmt.Println(<-ch) fmt.Println(<-ch)}


Fechamento de canais e range

package main

import "fmt"

func fibonacci(n int, ch chan int) { a, b := 0, 1 for i := 0; i < n; i++ { ch <-

Blogs

Book Recommendations