---
title: "Cheatsheet Go: Concorrência e Goroutines"
url: "https://golang.com.br/cheatsheet/concorrencia/"
markdown_url: "https://golang.com.br/cheatsheet/concorrencia.MD"
description: "Referência rápida de concorrência em Go: goroutines, channels, select, WaitGroup, Mutex, sync.Pool e padrões. Exemplos prontos para copiar e colar."
date: "2026-05-12"
author: "Golang Brasil"
---

# Cheatsheet Go: Concorrência e Goroutines

Referência rápida de concorrência em Go: goroutines, channels, select, WaitGroup, Mutex, sync.Pool e padrões. Exemplos prontos para copiar e colar.


# Concorrência em Go — Referência Rápida

Go foi projetada com concorrência como recurso de primeira classe. Esta referência cobre goroutines, channels, o pacote `sync` e padrões comuns. Para um tutorial aprofundado, veja [Concorrência em Go](/aprenda/concorrencia-go/) e [Padrões de Concorrência](/blog/padroes-concorrencia-go/).

---

## Goroutines

Uma goroutine é uma thread leve gerenciada pelo runtime do Go (~2 KB de stack inicial). Veja o [glossário de goroutines](/glossario/goroutine/) para mais contexto.

```go
package main

import (
	"fmt"
	"sync"
)

func trabalho(id int) {
	fmt.Printf("goroutine %d executando\n", id)
}

func main() {
	// Iniciar goroutine com função nomeada
	go trabalho(1)

	// Goroutine anônima
	go func(msg string) {
		fmt.Println(msg)
	}("olá da goroutine")

	// Esperar com WaitGroup
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(n int) {
			defer wg.Done()
			fmt.Printf("worker %d\n", n)
		}(i)
	}
	wg.Wait()
}
```

| Operação | Sintaxe |
|----------|---------|
| Criar goroutine | `go func()` |
| Goroutine anônima | `go func() { ... }()` |
| WaitGroup.Add | `wg.Add(1)` antes de `go` |
| WaitGroup.Done | `defer wg.Done()` dentro da goroutine |
| WaitGroup.Wait | `wg.Wait()` bloqueia até counter = 0 |

---

## Channels

Channels são a principal forma de comunicação entre goroutines. Consulte [channels no glossário](/glossario/channel/) para a teoria.

### Tipos de Channels

| Tipo | Sintaxe | Comportamento |
|------|---------|---------------|
| Sem buffer | `make(chan int)` | Bloqueia até sender e receiver estejam prontos |
| Com buffer | `make(chan int, 10)` | Bloqueia apenas quando o buffer está cheio/vazio |
| Somente envio | `chan<- int` | Só pode enviar |
| Somente recepção | `<-chan int` | Só pode receber |

### Operações Básicas

```go
package main

import "fmt"

func main() {
	// Channel sem buffer
	ch := make(chan string)

	go func() {
		ch <- "mensagem" // envia
	}()

	msg := <-ch // recebe (bloqueia até ter valor)
	fmt.Println(msg)

	// Channel com buffer
	buf := make(chan int, 3)
	buf <- 1
	buf <- 2
	buf <- 3
	// buf <- 4 // bloquearia — buffer cheio

	fmt.Println(<-buf) // 1
	fmt.Println(<-buf) // 2
}
```

### Fechar e Iterar sobre Channels

```go
package main

import "fmt"

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

func main() {
	for v := range gerador(5) {
		fmt.Println(v)
	}

	// Verificar se channel foi fechado
	ch := make(chan int, 1)
	ch <- 42
	close(ch)
	v, ok := <-ch
	fmt.Println(v, ok) // 42 true
	v, ok = <-ch
	fmt.Println(v, ok) // 0 false
}
```

---

## Select

`select` permite esperar em múltiplos channels simultaneamente. Veja [select no glossário](/glossario/select/).

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go func() {
		time.Sleep(100 * time.Millisecond)
		ch1 <- "canal 1"
	}()
	go func() {
		time.Sleep(200 * time.Millisecond)
		ch2 <- "canal 2"
	}()

	// Espera o primeiro que estiver pronto
	select {
	case msg := <-ch1:
		fmt.Println(msg)
	case msg := <-ch2:
		fmt.Println(msg)
	case <-time.After(1 * time.Second):
		fmt.Println("timeout")
	}

	// Select com default (não-bloqueante)
	select {
	case msg := <-ch1:
		fmt.Println(msg)
	default:
		fmt.Println("nenhum canal pronto")
	}
}
```

| Padrão | Uso |
|--------|-----|
| `select` com `time.After` | Timeout em operações |
| `select` com `default` | Operação não-bloqueante |
| `select` com `ctx.Done()` | Cancelamento via [context](/glossario/context/) |
| `select` em loop `for` | Listener contínuo |

---

## Pacote sync

### Mutex

Use [Mutex](/glossario/mutex/) para proteger dados compartilhados contra [race conditions](/erros/race-condition-data-race/).

```go
package main

import (
	"fmt"
	"sync"
)

type Contador struct {
	mu    sync.Mutex
	valor int
}

func (c *Contador) Incrementar() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.valor++
}

func (c *Contador) Valor() int {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.valor
}

func main() {
	c := &Contador{}
	var wg sync.WaitGroup

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			c.Incrementar()
		}()
	}

	wg.Wait()
	fmt.Println(c.Valor()) // 1000
}
```

### RWMutex

```go
package main

import "sync"

type Cache struct {
	mu   sync.RWMutex
	data map[string]string
}

func (c *Cache) Get(key string) (string, bool) {
	c.mu.RLock() // múltiplos leitores simultâneos
	defer c.mu.RUnlock()
	v, ok := c.data[key]
	return v, ok
}

func (c *Cache) Set(key, value string) {
	c.mu.Lock() // exclusivo para escrita
	defer c.mu.Unlock()
	c.data[key] = value
}
```

| Tipo | Descrição | Quando usar |
|------|-----------|-------------|
| `sync.Mutex` | Exclusão mútua | Leitura e escrita frequentes |
| `sync.RWMutex` | Leitura concorrente, escrita exclusiva | Muitas leituras, poucas escritas |

### sync.Once, sync.Pool e sync.Map

```go
package main

import (
	"fmt"
	"sync"
)

// Once — executa uma função uma única vez
var once sync.Once

func inicializar() {
	once.Do(func() {
		fmt.Println("inicializado (só executa uma vez)")
	})
}

// Pool — reutilização de objetos temporários
var bufPool = sync.Pool{
	New: func() any {
		return make([]byte, 0, 1024)
	},
}

func usarPool() {
	buf := bufPool.Get().([]byte)
	defer bufPool.Put(buf[:0])
	// usar buf...
}

// Map — map thread-safe (para chaves estáveis)
var cache sync.Map

func usarMap() {
	cache.Store("chave", "valor")
	v, ok := cache.Load("chave")
	fmt.Println(v, ok)
	cache.Delete("chave")
}

func main() {
	inicializar()
	inicializar() // não executa de novo
	usarPool()
	usarMap()
}
```

| Tipo | Uso principal |
|------|---------------|
| `sync.Once` | Inicialização lazy (singletons, config) |
| `sync.Pool` | Cache de objetos temporários (buffers, conexões) |
| `sync.Map` | Map concorrente — use para chaves que raramente mudam |

---

## Padrões Comuns de Concorrência

### Fan-Out / Fan-In

```go
package main

import (
	"fmt"
	"sync"
)

func fanOut(input <-chan int, workers int) []<-chan int {
	channels := make([]<-chan int, workers)
	for i := 0; i < workers; i++ {
		ch := make(chan int)
		channels[i] = ch
		go func(in <-chan int, out chan<- int) {
			for v := range in {
				out <- v * 2 // processa
			}
			close(out)
		}(input, ch)
	}
	return channels
}

func fanIn(channels ...<-chan int) <-chan int {
	out := make(chan int)
	var wg sync.WaitGroup
	for _, ch := range channels {
		wg.Add(1)
		go func(c <-chan int) {
			defer wg.Done()
			for v := range c {
				out <- v
			}
		}(ch)
	}
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

func main() {
	input := make(chan int, 5)
	for i := 1; i <= 5; i++ {
		input <- i
	}
	close(input)

	workers := fanOut(input, 3)
	for result := range fanIn(workers...) {
		fmt.Println(result)
	}
}
```

### Pipeline

```go
package main

import "fmt"

func gerar(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() {
	for v := range quadrado(gerar(2, 3, 4)) {
		fmt.Println(v)
	}
}
```

| Padrão | Descrição | Link |
|--------|-----------|------|
| Fan-Out/Fan-In | Distribui trabalho e agrega resultados | [Padrões de concorrência](/blog/padroes-concorrencia-go/) |
| Pipeline | Estágios conectados por channels | [Concorrência avançada](/tutoriais/go-concurrency-patterns/) |
| Worker Pool | Pool de goroutines com channel de jobs | [Concorrência em Go](/aprenda/concorrencia-go/) |
| Context cancelamento | Propagar cancelamento/timeout | [Context no glossário](/glossario/context/) |
| Semaphore | Limitar concorrência com buffered channel | [Testes de concorrência](/aprenda/testes-go/) |

---

## Detectar Problemas

| Ferramenta | Comando | Detecta |
|------------|---------|---------|
| Race detector | `go test -race ./...` | [Data races](/erros/race-condition-data-race/) |
| Deadlock | Runtime panic automático | [Deadlocks](/erros/deadlock-goroutines/) |
| Goroutine leak | `runtime.NumGoroutine()` | Goroutines que nunca terminam |
| pprof | `go tool pprof` | Perfil de goroutines em execução |

Veja o [cheatsheet de testes e debug](/cheatsheet/testing-debug/) para comandos detalhados de profiling. Para comparar modelos de concorrência, veja como <a href="https://rustlang.com.br/blog/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a> usa async/await com Tokio e como <a href="https://ziglang.com.br/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'ziglang.com.br' })">Zig</a> implementa concorrência com async frames sem alocação no heap.

---

## Veja Também

- [Cheatsheet: Sintaxe Básica](/cheatsheet/sintaxe-basica/) — Variáveis, tipos e funções
- [Cheatsheet: Slices, Maps e Structs](/cheatsheet/slices-maps-structs/) — Estruturas de dados
- [Tutorial: Concorrência em Go](/aprenda/concorrencia-go/) — Guia completo com exemplos
- [Tutorial: Padrões de Concorrência](/tutoriais/go-concurrency-patterns/) — Fan-out, pipelines e mais
- [Erro: Deadlock em Go](/erros/deadlock-goroutines/) — Como resolver deadlocks
- [Erro: Race Condition](/erros/race-condition-data-race/) — Detectar e corrigir data races
