---
title: "Concorrência em Go: Goroutines e Channels"
url: "https://golang.com.br/aprenda/concorrencia-go/"
markdown_url: "https://golang.com.br/aprenda/concorrencia-go.MD"
description: "Aprenda concorrência em Go: goroutines (100k simultâneas), channels, select, WaitGroup e padrões fan-in/fan-out. Com exemplos que rodam no playground."
date: "2026-01-30"
author: ""
---

# Concorrência em Go: Goroutines e Channels

Aprenda concorrência em Go: goroutines (100k simultâneas), channels, select, WaitGroup e padrões fan-in/fan-out. Com exemplos que rodam no playground.


# Concorrência em Go

Uma das maiores forças do Go é seu modelo de concorrência, baseado em **goroutines** e **channels**. Diferente de threads tradicionais, goroutines são leves e gerenciadas pelo runtime do Go.

## Goroutines

Uma goroutine é uma função executando concorrentemente com outras goroutines. Para criar uma goroutine, use a palavra-chave `go`:

```go
package main

import (
    "fmt"
    "time"
)

func dizer(msg string) {
    for i := 0; i < 3; i++ {
        fmt.Println(msg)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go dizer("goroutine")  // executa concorrentemente
    dizer("main")          // executa na goroutine principal
}
```

### Características das Goroutines

- **Leves**: ~2KB de stack (vs ~1MB para threads OS)
- **Multiplexadas**: milhares podem rodar em poucos threads OS
- **Gerenciadas pelo runtime**: scheduling automático

```go
// Criar 1000 goroutines é trivial
for i := 0; i < 1000; i++ {
    go func(n int) {
        fmt.Println(n)
    }(i)
}
```

## Channels

Channels são a forma idiomática de comunicação entre goroutines. Seguem o princípio:

> "Não comunique compartilhando memória; compartilhe memória comunicando."

### Criando e Usando Channels

```go
// Criar channel
ch := make(chan int)

// Enviar valor
go func() {
    ch <- 42  // envia 42 para o channel
}()

// Receber valor
valor := <-ch  // recebe do channel
fmt.Println(valor)  // 42
```

### Channels Bufferizados

```go
// Channel com buffer de tamanho 3
ch := make(chan int, 3)

ch <- 1  // não bloqueia
ch <- 2  // não bloqueia
ch <- 3  // não bloqueia
ch <- 4  // BLOQUEIA (buffer cheio)
```

### Direção de Channels

```go
// Apenas envio
func produtor(ch chan<- int) {
    ch <- 1
}

// Apenas recebimento
func consumidor(ch <-chan int) {
    valor := <-ch
}
```

### Fechando Channels

```go
ch := make(chan int)

go func() {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)  // sinaliza que não há mais valores
}()

// Range itera até o channel fechar
for v := range ch {
    fmt.Println(v)
}
```

### Verificando se Channel está Fechado

```go
valor, ok := <-ch
if !ok {
    fmt.Println("channel fechado")
}
```

## Select

O `select` permite esperar em múltiplos channels:

```go
select {
case msg1 := <-ch1:
    fmt.Println("recebeu de ch1:", msg1)
case msg2 := <-ch2:
    fmt.Println("recebeu de ch2:", msg2)
case ch3 <- valor:
    fmt.Println("enviou para ch3")
default:
    fmt.Println("nenhum channel pronto")
}
```

### Timeout com Select

```go
select {
case res := <-ch:
    fmt.Println(res)
case <-time.After(1 * time.Second):
    fmt.Println("timeout!")
}
```

## Padrões Comuns

### Worker Pool

```go
func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("worker %d processando job %d\n", id, j)
        time.Sleep(time.Second)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // Criar 3 workers
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // Enviar 5 jobs
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    // Coletar resultados
    for a := 1; a <= 5; a++ {
        <-results
    }
}
```

### Fan-out / Fan-in

```go
// Fan-out: uma fonte, múltiplos consumidores
func fanOut(input <-chan int, workers int) []<-chan int {
    outputs := make([]<-chan int, workers)
    for i := 0; i < workers; i++ {
        outputs[i] = processador(input)
    }
    return outputs
}

// Fan-in: múltiplas fontes, um consumidor
func fanIn(inputs ...<-chan int) <-chan int {
    out := make(chan int)
    var wg sync.WaitGroup
    
    for _, in := range inputs {
        wg.Add(1)
        go func(ch <-chan int) {
            defer wg.Done()
            for v := range ch {
                out <- v
            }
        }(in)
    }
    
    go func() {
        wg.Wait()
        close(out)
    }()
    
    return out
}
```

### Pipeline

```go
func gerador(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}

func quadrado(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {
            out <- n * n
        }
        close(out)
    }()
    return out
}

func main() {
    // Pipeline: gerador -> quadrado
    for v := range quadrado(gerador(1, 2, 3, 4)) {
        fmt.Println(v)  // 1, 4, 9, 16
    }
}
```

## sync.WaitGroup

Para esperar múltiplas goroutines terminarem:

```go
var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(n int) {
        defer wg.Done()
        fmt.Println(n)
    }(i)
}

wg.Wait()  // espera todas terminarem
```

## Armadilhas Comuns

### 1. Race Condition

```go
// ERRADO - race condition
counter := 0
for i := 0; i < 1000; i++ {
    go func() {
        counter++  // múltiplas goroutines acessando
    }()
}

// CORRETO - usando mutex
var mu sync.Mutex
counter := 0
for i := 0; i < 1000; i++ {
    go func() {
        mu.Lock()
        counter++
        mu.Unlock()
    }()
}
```

### 2. Goroutine Leak

```go
// ERRADO - goroutine nunca termina
func leak() {
    ch := make(chan int)
    go func() {
        val := <-ch  // bloqueia para sempre
        fmt.Println(val)
    }()
    // ch nunca recebe valor, goroutine fica pendurada
}

// CORRETO - usar contexto para cancelamento
func semLeak(ctx context.Context) {
    ch := make(chan int)
    go func() {
        select {
        case val := <-ch:
            fmt.Println(val)
        case <-ctx.Done():
            return  // sai quando contexto cancela
        }
    }()
}
```

### 3. Fechar Channel do Lado Errado

```go
// REGRA: quem ENVIA fecha o channel
// Nunca feche um channel do lado receptor
```

## Detectando Race Conditions

```bash
# Build com detector de race
go build -race ./...

# Testar com detector de race
go test -race ./...
```

## Recursos

- [Go Concurrency Patterns](https://go.dev/blog/pipelines)
- [Advanced Go Concurrency Patterns](https://go.dev/blog/advanced-go-concurrency-patterns)
- [The Go Memory Model](https://go.dev/ref/mem)

Para uma perspectiva diferente sobre concorrência, compare com o modelo async/await em <a href="https://rustlang.com.br/artigos/rust-vs-go/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust vs Go</a> (baseado em futures e Tokio) e com <a href="https://kotlin.dev.br/blog/coroutines-avancadas-structured-concurrency-kotlin/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">structured concurrency em Kotlin</a> — cada linguagem resolve o mesmo problema de formas fundamentalmente distintas.

---

*Última atualização: Janeiro 2026*

---

## Veja também

- [WebSocket com Go](/aprenda/golang-websocket/) — Use concorrência num chat real-time
- [Microservices com Go](/aprenda/golang-microservices/) — Concorrência em microservices
- [Tratamento de Erros](/aprenda/golang-erros/) — Erros em código concorrente
- [Go Cheatsheet](/cheatsheet/) — Referência rápida de goroutines e channels
- [Vagas Go](/vagas/) — Empresas que buscam expertise em concorrência
