Canais: Bufferizados, Não Bufferizados e Regras de Fechamento
Canais transportam valores e sincronização entre goroutines.
Busque em todas as páginas da documentação
Canais transportam valores e sincronização entre goroutines.
Escolher entre capacidade bufferizada e não bufferizada e seguir as regras de propriedade de fechamento previne deadlocks e panics em pipelines de produção.
Um canal não bufferizado sincroniza os parceiros: o remetente bloqueia até que o receptor esteja pronto.
Um canal bufferizado armazena até cap valores sem um receptor esperando.
Fechar um canal transmite "não mais envios" para os receptores; apenas remetentes devem fechar.
range em um canal drena até o fechamento; o recebimento de dois valores v, ok := <-ch detecta o fechamento explicitamente.
Cartão de receita de referência rápida - pronto para copiar e colar.
package main
import "fmt"
func main() {
ch := make(chan int, 2) // bufferizado
ch <- 1
ch <- 2
close(ch)
for v := range ch {
fmt.Println(v)
}
v, ok := <-ch
fmt.Println(v, ok) // 0 false após drenar o canal fechado
}Quando usar isso:
range.package main
import (
"fmt"
"sync"
)
func producer(out chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 1; i <= 5; i++ {
out <- i
}
close(out) // remetente fecha
}
func consumer(in <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for v := range in {
fmt.Println("got", v)
}
}
func main() {
ch := make(chan int) // não bufferizado - passagem de controle síncrona
var wg sync.WaitGroup
wg.Add(2)
go producer(ch, &wg)
go consumer(ch, &wg)
wg.Wait()
}O que isso demonstra:
chan<- e <-chan documentam o uso apenas para envio/recebimento.range no consumidor termine.WaitGroup espera que ambos os lados terminem.close(ch) define a flag de fechado; envios futuros causam panic, recebimentos retornam o valor zero com ok == false.len(ch) e cap(ch) reportam os elementos enfileirados e a capacidade.| Tipo | Envio bloqueia quando | Recebimento bloqueia quando | Força da Sincronização |
|---|---|---|---|
| Não Bufferizado | Nenhum receptor pronto | Nenhum remetente pronto | Forte (rendezvous) |
| Bufferizado | Buffer cheio | Buffer vazio | Mais fraco até encher |
| Regra | Racional |
|---|---|
| Remetente fecha | Receptores não devem sinalizar a conclusão do produtor |
| Fechar uma vez | Panics de fechamento duplo |
Receptores detectam via ok ou range | Desligamento limpo dos loops do consumidor |
| Não fechar canais de trabalho cedo | Consumidores ainda podem esperar valores |
// Fan-in: uma goroutine fecha após todos os remetentes terminarem
func fanIn(inputs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(inputs))
for _, in := range inputs {
go func(c <-chan int) {
defer wg.Done()
for v := range c {
out <- v
}
}(in)
}
go func() {
wg.Wait()
close(out)
}()
return out
}sync.Once para proteger o fechamento.context cancel, select com timeout, ou buffer.WaitGroup e depois fechar).
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Canal não bufferizado | Necessita de passagem de controle estrita | Alto throughput com taxas incompatíveis |
| Canal bufferizado | Absorção de picos | Necessita de sinais de pressão de retorno imediatos |
sync.Mutex + fila de slice | Fila compartilhada em memória | Transferência de propriedade entre goroutines é mais clara com canais |
Apenas context | Sinalização de cancelamento | Passagem de itens de trabalho (use canais) |
Comece com 0 (não bufferizado) ou um pequeno múltiplo do número de workers.
Faça benchmark e observe a profundidade da fila sob carga; buffers superdimensionados escondem a sobrecarga.
Sim - envios são seguros concorrentemente.
Coordene o fechamento para que apenas uma goroutine feche após todos os remetentes terminarem.
Bloqueia para sempre.
Use um canal nil em select para desabilitar um caso dinamicamente.
Receptores bloqueados para sempre mantêm os remetentes vivos independentemente do fechamento.
O fechamento é para semântica e término de range, não primariamente para GC.
Tecnicamente possível em exemplos simples; em pipelines, quebra a propriedade.
Receptores devem sair ao fechar, não iniciá-lo.
nil - operações em canais nil bloqueiam para sempre (útil em truques de select).
chan<- T e <-chan T em assinaturas de função documentam a intenção e previnem recebimento/envio acidental em tempo de compilação.
Sim - cada recebimento copia.
Envie ponteiros se estiver mutando structs compartilhadas (com sincronização).
Receptores drenam valores bufferizados após o fechamento, e então recebem ok == false.
Um envio em um canal acontece-antes do recebimento correspondente ser concluído.
Essa aresta faz parte do modelo de memória do Go.
Versões de Stack: Esta página foi escrita para Go 1.26.x (GC padrão 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: 19 de jul. de 2026