Concorrência em Go: Goroutines e Canais Primeiro
Go trata a concorrência como um recurso de primeira classe da linguagem.
Busque em todas as páginas da documentação
Go trata a concorrência como um recurso de primeira classe da linguagem.
Você inicia trabalho com go, coordena com canais e recorre a primitivas de sync apenas quando o estado compartilhado é o modelo mais simples.
Esta página é a âncora conceitual para a seção de Concorrência.
Noções Básicas de Concorrência reúne snippets executáveis; artigos irmãos cobrem agendamento, regras de fechamento de canal, select, mutexes e o detector de corridas (race detector).
Imagine um programa Go como um conjunto de goroutines - tarefas leves agendadas pelo runtime - que passam dados através de canais.
Um canal é um conduto tipado: chan int carrega valores int entre goroutines.
Envio (ch <- v) e recebimento (v := <-ch) são os pontos de sincronização.
Um canal não bufferizado bloqueia o remetente até que um receptor esteja pronto e bloqueia o receptor até que um remetente chegue.
Esse handshake é um evento de sincronização: o envio acontece antes do recebimento ser concluído.
Um canal bufferizado desacopla remetente e receptor até sua capacidade; envios bloqueiam apenas quando o buffer está cheio, recebimentos apenas quando está vazio.
O famoso provérbio de Go - Não comunique compartilhando memória; compartilhe memória comunicando - é um conselho prático, não uma proibição de mutexes.
Significa: prefira passar a propriedade de dados através de canais para que apenas uma goroutine modifique um valor por vez.
Quando várias goroutines precisam ler e atualizar o mesmo mapa ou contador, sync.Mutex ou sync/atomic é frequentemente a ferramenta certa.
Iniciar uma goroutine é go f() ou go func() { ... }().
A goroutine principal é o ponto de entrada do programa; quando main retorna, o processo termina e as goroutines pendentes são terminadas sem esperar.
Use sync.WaitGroup, canais ou context para coordenar o desligamento.
O scheduler do runtime usa o modelo GPM: G (goroutine), P (processador lógico que detém uma fila de execução), M (thread do SO).
Os Ps são vinculados aos Ms; quando uma goroutine bloqueia em I/O ou em um canal, o scheduler a estaciona e executa outra G no mesmo M.
É por isso que você pode gerar milhares de goroutines onde threads do SO ficariam sobrecarregadas.
select multiplexa operações de canal: ele bloqueia até que um caso possa prosseguir, ou executa default para uma tentativa não bloqueante.
Timeouts são idiomáticos: select entre o trabalho e <-time.After(d).
Fechar um canal sinaliza "não mais valores"; receptores aprendem através da forma de dois valores v, ok := <-ch onde ok é falso após o fechamento.
Apenas o remetente deve fechar - enviar em um canal fechado causa pânico.
goroutine principal
│
├── go worker ──► ch ──► go consumer
│ │
│ └── close(ch) quando terminar
│
└── wg.Wait() / drenar resultados
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Canal não bufferizado | Sincronização forte, handshake claro | Acopla o timing do produtor/receptor | Despacho de tarefas, pipelines de confirmação |
| Canal bufferizado | Suaviza picos | Oculta backpressure se mal dimensionado | Fan-in de logs, filas limitadas |
sync.Mutex | Atualizações simples de struct compartilhada | Fácil de causar deadlock se aninhado incorretamente | Caches, índices em memória |
sync/atomic | Contadores/flags rápidos | Limitado a operações numéricas/de ponteiro | Métricas, contagens de referência |
select + default | Sondagens não bloqueantes | Loops ocupados se mal utilizados | Timeouts, tentar enviar/receber |
Servidores HTTP em chi, gin e echo tratam cada requisição em sua própria goroutine - a concorrência é a forma padrão do código de rede Go.
Streams e pools de workers do google.golang.org/grpc empilham canais e cancelamento de contexto sob os manipuladores de RPC.
Reconciliadores do controller-runtime rodam concorrentemente; caches de informer compartilhados usam mutexes internamente enquanto eventos fluem através de canais.
Serviços de produção combinam padrões: pools de workers limitados controlam o paralelismo, context.Context propaga prazos e errgroup cancela irmãos no primeiro erro (abordado em Concorrência Avançada).
Execute testes e builds de CI com -race para capturar escritas de mapa não sincronizadas e bugs de verificar-depois-agir.
golangci-lint inclui verificações de padrões propensos a corridas; combine linters com testes de integração que exercitem caminhos concorrentes.
Faça profiling antes de gerar goroutines ilimitadas por item - a memória para pilhas e a sobrecarga do scheduler se acumulam sob fan-out extremo.
ok ou range.Processos Sequenciais Comunicantes (Communicating Sequential Processes) é um modelo onde processos independentes trocam mensagens.
As goroutines e canais de Go implementam essa ideia sem uma sintaxe de processo separada - a concorrência vive na linguagem.
Prefira canais ao passar propriedade ou itens de trabalho entre estágios.
Prefira mutexes quando várias goroutines atualizam os mesmos campos de struct frequentemente.
O retorno de main encerra o processo.
Espere com sync.WaitGroup, drene um canal de resultados ou bloqueie em select até que os workers sinalizem que terminaram.
O remetente bloqueia para sempre - um deadlock se nenhuma outra goroutine puder receber.
Sempre emparelhe remetentes com receptores ou use buffering/timeouts.
Apenas se uma goroutine escrever ou você sincronizar o acesso.
Passe uma cópia ou envie o slice através de um canal; corridas de append não sincronizadas são comuns.
Se múltiplos casos estiverem prontos, Go escolhe um pseudo-aleatoriamente.
Não confie na ordem de prioridade sem reestruturar o loop.
Não - GOMAXPROCS define Ps lógicos (execução paralela em threads do SO).
Você pode executar muito mais goroutines do que GOMAXPROCS.
Não - goroutines ajudam quando o trabalho pode se sobrepor (I/O, estágios paralelos).
Trabalho serial intensivo em CPU com entradas pequenas geralmente roda mais rápido sequencialmente.
O modelo de memória define arestas de ordenação: o envio de canal acontece antes do recebimento, mutex.Unlock acontece antes de um Lock posterior, etc.
Corridas violam essas arestas.
Não - o detector de corridas adiciona sobrecarga e é para testes e staging.
Envie builds sem race; execute -race em CI em pacotes que usam concorrência.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (última - verifique na compilação), gin (última - verifique na compilação), echo (última - verifique na compilação), google.golang.org/grpc (última - verifique na compilação), sigs.k8s.io/controller-runtime (última - verifique na compilação), kubebuilder (última - verifique na compilação), tinygo (última - verifique os alvos de placa na compilação), wazero (última - verifique na compilação) e golangci-lint (última - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026