Concorrência e Multithreading em Go: Nível Avançado

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

Concorrência e Multithreading em Go: Nível Avançado



Go (Golang) foi projetado desde o início como uma linguagem para escrever aplicações escaláveis e de alto desempenho. Na base disso está um poderoso modelo de concorrência, fundamentado em goroutines e canais. Se você já domina os exemplos básicos com go func() e canais simples, é hora de avançar para o próximo nível. Neste artigo, exploraremos padrões avançados de trabalho com concorrência em Go que o ajudarão a escrever código confiável, eficiente e seguro.



1. Gerenciamento do Ciclo de Vida das Goroutines: Context e WaitGroup



Um dos problemas mais comuns é o vazamento de goroutines. Uma goroutine que espera infinitamente por dados de um canal nunca será finalizada se o canal for fechado incorretamente. Para gerenciar o ciclo de vida das goroutines em Go, existem duas ferramentas principais: sync.WaitGroup e context.Context.



sync.WaitGroup para Aguardar a Conclusão

Use sync.WaitGroup quando precisar aguardar a conclusão de um conjunto fixo de goroutines. É um mecanismo simples e eficaz.



package main

import (\t"fmt"\t"sync"\t"time")

func worker(id int, wg *sync.WaitGroup) {\tdefer wg.Done() // Decrementa o contador ao sair\tfmt.Printf("Worker %d iniciou o trabalho", id)\ttime.Sleep(time.Second)\tfmt.Printf("Worker %d finalizou o trabalho", id)}

func main() {\tvar wg sync.WaitGroup

\tfor i := 1; i <= 3; i++ {\t\twg.Add(1) // Incrementa o contador antes de iniciar\t\tgo worker(i, &wg)\t}

\twg.Wait() // Bloqueia até o contador chegar a 0\tfmt.Println("Todos os workers concluíram")}


context.Context para Cancelamento e Prazos

Quando uma goroutine opera em um loop ou executa uma operação longa, ela precisa ser capaz de ser finalizada corretamente por um sinal externo. context.Context é a forma padrão de transmitir um sinal de cancelamento, prazo ou tempo limite.



package main

import (\t"context"\t"fmt"\t"time")

func longRunningTask(ctx context.Context) {\tfor {\t\tselect {\t\tcase <-ctx.Done():\t\t\tfmt.Println("Goroutine recebeu sinal de parada")\t\t\treturn\t\tdefault:\t\t\ttime.Sleep(500 * time.Millisecond)\t\t\tfmt.Println("Trabalhando...")\t\t}\t}}

func main() {\tctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\tdefer cancel() // Importante chamar cancel para liberar recursos

\tgo longRunningTask(ctx)

\ttime.Sleep(3 * time.Second)\tfmt.Println("Main finalizou")}


2. Canais: Bufferização, Fechamento e Padrão Fan-Out/Fan-In



Canais são a principal forma de sincronização e transferência de dados entre goroutines. Em nível avançado, é importante entender a diferença entre canais com buffer e sem buffer, fechá-los corretamente e usar padrões de agregação.



Com Buffer vs Sem Buffer

Um canal sem buffer (sem buffer) bloqueia o remetente até que o destinatário leia os dados. Um canal com buffer permite enviar várias mensagens sem esperar, até que o buffer esteja cheio. A escolha depende da tarefa: para sincronização rigorosa, use canais sem buffer; para suavizar a carga, use canais com buffer.



Padrão Fan-Out/Fan-In

Este é um dos padrões mais poderosos. Fan-Out é a distribuição de tarefas entre várias goroutines (workers). Fan-In é a coleta de resultados de várias goroutines em um único canal.



package main

import (\t"fmt"\t"sync")

func producer(nums ...int) <-chan int {\tout := make(chan int)\tgo func() {\t\tfor _, n := range nums {\t\t\tout <- n\t\t}\t\tclose(out)\t}()\treturn out}

func square(in <-chan int) <-chan int {\tout := make(chan int)\tgo func() {\t\tfor n := range in {\t\t\tout <- n * n\t\t}\t\tclose(out)\t}()\treturn out}

func merge(channels ...<-chan int) <-chan int {\t

Blogs

Book Recommendations