Extensões errgroup & golang.org/x/sync
Goroutines coordenadas com cancelamento no primeiro erro.
Busque em todas as páginas da documentação
Goroutines coordenadas com cancelamento no primeiro erro.
golang.org/x/sync/errgroup agrupa goroutines que compartilham o mesmo destino: uma falha cancela as demais e apresenta um único erro de Wait.
Pacotes relacionados em x/sync adicionam paralelismo limitado (SetLimit), deduplicação (singleflight) e semáforos de contagem (semaphore).
Essas extensões preenchem lacunas no pacote sync da biblioteca padrão para orquestração estilo serviço.
Cartão de receita de referência rápida - pronto para copiar e colar.
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(4)
g.Go(func() error { return stepA(ctx) })
g.Go(func() error { return stepB(ctx) })
if err := g.Wait(); err != nil {
return err
}Quando usar isso:
SetLimit).singleflight.Group.semaphore.Weighted.// go.mod: module example.com/errgroupdemo
package main
import (
"context"
"fmt"
"sync"
"time"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
"golang.org/x/sync/singleflight"
)
func fetch(ctx context.Context, name string, delay time.Duration, fail bool) error {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(delay):
if fail {
return fmt.Errorf("%s failed", name)
}
fmt.Println("fetched", name)
return nil
}
}
func parallelFetch(ctx context.Context) error {
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(2)
tasks := []struct {
name string
d time.Duration
fail bool
}{
{"users", 20 * time.Millisecond, false},
{"orders", 30 * time.Millisecond, false},
{"inventory", 10 * time.Millisecond, true},
}
for _, t := range tasks {
t := t
g.Go(func() error {
return fetch(ctx, t.name, t.d, t.fail)
})
}
return g.Wait()
}
func dedupeLoad(g *singleflight.Group, key string) (string, error) {
v, err, _ := g.Do(key, func() (any, error) {
time.Sleep(50 * time.Millisecond)
return "data-for-" + key, nil
})
if err != nil {
return "", err
}
return v.(string), nil
}
func main() {
ctx := context.Background()
if err := parallelFetch(ctx); err != nil {
fmt.Println("parallel:", err)
}
var sf singleflight.Group
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
s, _ := dedupeLoad(&sf, "account:42")
fmt.Println(s)
}()
}
wg.Wait()
sem := semaphore.NewWeighted(1)
if err := sem.Acquire(ctx, 1); err == nil {
defer sem.Release(1)
fmt.Println("critical section")
}
}O que isso demonstra:
WithContext vincula a vida útil da goroutine ao cancelamento compartilhado.SetLimit(2) impede mais de duas buscas simultaneamente.singleflight executa um carregador por chave enquanto os esperadores compartilham o resultado.semaphore.Weighted bloqueia com reconhecimento de contexto, ao contrário de truques de canal brutos.errgroup.Group incorpora um WaitGroup mais um slot de erro protegido por mutex.Go aciona cancel no contexto derivado de WithContext.SetLimit(n) usa um semáforo de canal interno; n < 1 significa ilimitado.singleflight.Do coalesça chamadas concorrentes com a mesma chave até que a função líder retorne.| Pacote | Tipo | Função |
|---|---|---|
| errgroup | Group | Tarefas paralelas, primeiro erro vence |
| semaphore | Weighted | Adquire/libera capacidade com ctx |
| singleflight | Group | Suprime trabalho duplicado em andamento |
| syncmap | Map | Mapa concorrente (nicho; prefira frequentemente mapa simples+mutex) |
// errgroup sem cancelamento - use quando falhas não devem parar irmãos
var g errgroup.Group
g.Go(func() error { return maybeFail() })
_ = g.Wait()errgroup.Group simples quando o sucesso parcial for aceitável e você mesclar os erros sozinho.ctx derivado de WithContext para I/O, não o pai.Go após falha de um irmão - O trabalho continua após o cancelamento. Correção: Use apenas o ctx derivado.Go após Wait - Comportamento indefinido; crie um novo Grupo por lote. Correção: Um grupo por operação lógica.ctx.Done() nos corpos de busca.Go - Trava o processo; errgroup não recupera. Correção: Mantenha os pânicos fora; use wrappers seguros em produção.sync.WaitGroup para junção simples.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
sync.WaitGroup + canal de erro | Lógica de mesclagem de erro personalizada | O cancelamento padrão no primeiro erro é suficiente |
parallel.For (iterador Go 1.22+) | Fatias de CPU com erros síncronos | I/O misto com cancelamento |
| Canal de pool de workers | Fila de longa duração | Lote paralelo de uso único |
context.WithCancel manual | Cancelamento parcial granular | Boilerplate sem benefício |
Retorno de erro automático e cancelamento opcional de contexto na primeira falha.
Quando qualquer falha deve parar as goroutines irmãs - típico para manipuladores de requisição que se ramificam.
Paralelismo ilimitado - o mesmo que não chamar SetLimit.
Use uma fatia com mutex, execute tarefas sequencialmente ou tipos de múltiplos erros de terceiros.
errgroup intencionalmente retorna um.
Não - ele apenas deduplica cargas concorrentes.
Combine com um cache real para resultados armazenados.
Apenas dentro de um processo.
Use bloqueios distribuídos ou cache para deduplicação entre instâncias.
Inicie uma tarefa lenta e uma tarefa rápida que falha; afirme que a lenta retorna context.Canceled.
Semaphore suporta acquire ponderado, cancelamento de contexto e API mais clara para limites dinâmicos.
Frequentemente sim para helpers de envio/recebimento concorrentes vinculados ao ciclo de vida do stream.
É um módulo de extensão oficial do Go versionado separadamente da biblioteca padrão.
Fixe em go.mod e verifique no momento da CI.
Derive o WithContext filho do ctx pai para que o cancelamento externo ainda se aplique.
As goroutines ainda rodam, mas vazamentos são possíveis se elas bloquearem para sempre - sempre chame Wait.
Versões da 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