← Ver a árvore completa

ROTA · 6 CLASSES · 1 GRUPOS

Concorrência Prática: Worker Pools

Organize filas, pools, pipelines e cancelamento de trabalho concorrente.

Pré-requisitos orientam, mas nunca bloqueiam. Cinco classes fundamentais e o bônus Go 1.27 são grátis com conta.

O ARCO DA ROTA

01Concorrência

ORDEM RECOMENDADA

6 classes, do primeiro passo ao fechamento.

Cada classe leva cerca de 15–25 minutos no novo formato de slides. Você pode sair da ordem quando outro nó fizer mais sentido.

  1. 01
    Concorrência Prática: Worker PoolsGoroutines e channels: coordenação sem adivinhação

    Recupere goroutines, channels e WaitGroup construindo cálculos concorrentes sem data races nem ordem acidental.

  2. 02
    Concorrência Prática: Worker PoolsChannels como fila: capacidade, fechamento e drenagem

    Use channels como filas FIFO, entenda buffer e backpressure e estabeleça fechamento sem deadlocks.

  3. 03
    Concorrência Prática: Worker PoolsO worker pool: concorrência limitada e resultados ordenados

    Construa um worker pool de tamanho fixo, limite recursos e devolva sucessos e erros em ordem determinística.

  4. 04
    Concorrência Prática: Worker PoolsPipelines: estágios pequenos, fechamento em cadeia

    Componha fonte, filtro, transformação e fan-in com fechamento em cadeia e ordenação explícita na borda.

  5. 05
    Concorrência Prática: Worker PoolsCancelamento com context: trabalho que sabe parar

    Propague context pelo pool, desbloqueie envios cancelados e teste interrupção sem depender de relógio.

  6. 06
    Concorrência Prática: Worker PoolsCapstone: processador paralelo de registros

    Entregue um importador paralelo cancelável que normaliza linhas, preserva erros por registro e gera relatório auditável.

PRÉVIA DA PRIMEIRA CLASSE

Goroutines e channels: coordenação sem adivinhação

jobs := make(chan int, len(values))
for index := range values { jobs <- index } // Cada job leva o destino.
close(jobs)
results := make([]int, len(values))
var group sync.WaitGroup
group.Add(workers)
for range workers {
	go func() {
		defer group.Done()
		for index := range jobs { results[index] = values[index] * values[index] } // Índice fixa a ordem.
	}()
}
group.Wait() // Barreira antes de ler results.

Uma cozinha recebe comandas numeradas: 17, 18 e 19. Dois cozinheiros podem preparar pratos em paralelo, mas o garçom precisa entregar cada prato à mesa indicada, não à primeira mesa livre. O número da comanda é o dado que permite separar ordem de execução de ordem do resultado. Goroutines são os cozinheiros; o índice do slice é a comanda.

Iniciar uma função com go apenas autoriza execução concorrente. Não garante que ela comece antes da próxima linha, não faz main esperá-la e não protege variáveis compartilhadas. Se duas goroutines executam total += value, a operação de leitura, soma e escrita pode se intercalar: isso é uma data race, acesso concorrente incompatível ao mesmo estado.

Um channel cria um ponto de encontro. Em um channel sem buffer, o envio só termina quando algum receptor aceita o valor. close(jobs) acrescenta outro sinal: não haverá novas comandas. Workers usam range jobs e saem quando a fila foi fechada e drenada. O fechamento não espera o cálculo atual terminar, por isso o programa ainda precisa de sync.WaitGroup como barreira final.

Para resultados independentes, pré-alocamos results e fazemos cada job carregar index. Dois workers podem concluir em qualquer ordem, mas nunca escrevem na mesma posição. Para uma redução, como soma, damos um acumulador exclusivo a cada worker e combinamos as parciais somente depois de Wait. Nos dois desenhos, a sincronização segue fatos observáveis, não uma estimativa de duração.

O teste de sanidade é trocar dois por oito workers e repetir a entrada. A distribuição interna provavelmente muda; [4 9 16] e o total 29 não. Quando a API devolve o mesmo resultado sob todos os escalonamentos válidos, a concorrência deixou de vazar para o contrato.

Antes de rodar, some mentalmente os quadrados: o resultado deve ser 56, embora a ordem em que chegam a out não seja contratada. O fechamento acontece numa goroutine separada porque main precisa continuar recebendo; se chamasse group.Wait() antes do range, workers poderiam bloquear tentando enviar e ninguém os drenaria.

package main

import (
	"fmt"
	"sync"
)

func main() {
	values := []int{2, 4, 6}
	out := make(chan int)
	var group sync.WaitGroup
	for _, value := range values {
		group.Add(1)
		go func(n int) {
			defer group.Done()
			out <- n * n
		}(value)
	}
	go func() {
		group.Wait()
		close(out)
	}()
	total := 0
	for square := range out {
		total += square
	}
	fmt.Println(total)
}
A introdução termina aqui

Entre para continuar nos slides, rodar o código e enfrentar os desafios.

Começar pela rota →

ESTA ROTA E TODAS AS OUTRAS

Qualquer plano abre os 192 nós.

Mensal R$19,90 · anual R$149 · lifetime R$499. Todos têm garantia de 7 dias.

Comparar os 3 planos →Começar pelas classes grátis