🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

Guia Go Concurrency 2026: Goroutines, canais e padrões de produção

⏱️5 min read  ·  967 words

O modelo de simultaneidade do Go – goroutines e canais – é o sistema de simultaneidade mais simples e poderoso em qualquer linguagem convencional. Em 2026, o Go 1.23 com iteradores de intervalo sobre função e registro estruturado de slog torna o código Go simultâneo mais limpo do que nunca. Este guia cobre tudo, desde goroutines básicas até padrões de produção.

Goroutines — Threads Leves

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	// goroutine = lightweight thread (starts with 2KB stack, not 1MB like OS threads)
	go sayHello("World")     // fire and forget

	// Wait for goroutine using WaitGroup
	var wg sync.WaitGroup

	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			fmt.Printf("Worker %d done
", id)
			time.Sleep(time.Duration(id) * time.Millisecond)
		}(i)
	}

	wg.Wait() // block until all goroutines finish
	fmt.Println("All workers done")
}

func sayHello(name string) {
	time.Sleep(10 * time.Millisecond)
	fmt.Printf("Hello, %s!
", name)
}

Canais — Comunicação entre Goroutines

package main

import "fmt"

func main() {
	// Unbuffered channel — sender blocks until receiver ready
	ch := make(chan int)

	go func() {
		ch <- 42  // send
	}()

	value := <-ch  // receive
	fmt.Println(value)  // 42

	// Buffered channel — sender blocks only when buffer full
	buffered := make(chan string, 5)
	buffered <- "hello"
	buffered <- "world"
	// Doesn't block because buffer has space
	close(buffered)

	// Range over closed channel
	for msg := range buffered {
		fmt.Println(msg)
	}

	// Directional channels in function signatures
	produce := func(ch chan<- int) {  // send-only
		for i := 0; i < 5; i++ {
			ch <- i
		}
		close(ch)
	}

	consume := func(ch <-chan int) {  // receive-only
		for n := range ch {
			fmt.Println("received:", n)
		}
	}

	dataCh := make(chan int, 10)
	go produce(dataCh)
	consume(dataCh)
}

Selecione – Canais de Multiplexação

package main

import (
	"context"
	"fmt"
	"time"
)

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

	go func() {
		time.Sleep(100 * time.Millisecond)
		ch1 <- "from ch1"
	}()

	go func() {
		time.Sleep(200 * time.Millisecond)
		ch2 <- "from ch2"
	}()

	// Select picks whichever channel is ready first
	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-ch1:
			fmt.Println(msg1)
		case msg2 := <-ch2:
			fmt.Println(msg2)
		}
	}

	// With timeout
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	select {
	case result := <-fetchData():
		fmt.Println(result)
	case <-ctx.Done():
		fmt.Println("timeout:", ctx.Err())
	}

	// Non-blocking send with default
	ch := make(chan int, 1)
	select {
	case ch <- 42:
		fmt.Println("sent")
	default:
		fmt.Println("channel full, skipped")
	}
}

Padrão de pool de trabalhadores

package main

import (
	"fmt"
	"sync"
)

type Job struct {
	ID   int
	Data string
}

type Result struct {
	JobID  int
	Output string
	Err    error
}

func workerPool(numWorkers int, jobs <-chan Job, results chan<- Result) {
	var wg sync.WaitGroup
	for i := 0; i < numWorkers; i++ {
		wg.Add(1)
		go func(workerID int) {
			defer wg.Done()
			for job := range jobs {
				output, err := processJob(job)
				results <- Result{JobID: job.ID, Output: output, Err: err}
			}
		}(i)
	}

	// Close results channel when all workers done
	go func() {
		wg.Wait()
		close(results)
	}()
}

func processJob(job Job) (string, error) {
	return fmt.Sprintf("processed: %s", job.Data), nil
}

func main() {
	jobs := make(chan Job, 100)
	results := make(chan Result, 100)

	// Start 5 workers
	go workerPool(5, jobs, results)

	// Send 20 jobs
	go func() {
		for i := 0; i < 20; i++ {
			jobs <- Job{ID: i, Data: fmt.Sprintf("item-%d", i)}
		}
		close(jobs)
	}()

	// Collect results
	for result := range results {
		if result.Err != nil {
			fmt.Printf("Job %d failed: %v
", result.JobID, result.Err)
			continue
		}
		fmt.Printf("Job %d: %s
", result.JobID, result.Output)
	}
}

Contexto — Cancelamento e Prazos

package main

import (
	"context"
	"fmt"
	"time"
)

func fetchWithContext(ctx context.Context, url string) (string, error) {
	// Check if context already cancelled
	select {
	case <-ctx.Done():
		return "", ctx.Err()
	default:
	}

	// Simulate slow operation
	done := make(chan string, 1)
	go func() {
		time.Sleep(500 * time.Millisecond)
		done <- "data from " + url
	}()

	select {
	case data := <-done:
		return data, nil
	case <-ctx.Done():
		return "", ctx.Err()
	}
}

func main() {
	// Context with deadline
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	data, err := fetchWithContext(ctx, "https://api.example.com/data")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println(data)

	// Pass context to database queries (Go standard pattern)
	// db.QueryContext(ctx, "SELECT ...")
	// http.NewRequestWithContext(ctx, "GET", url, nil)
}

pacote de sincronização – Mutexes e uma vez

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

// Mutex for shared state
type SafeCounter struct {
	mu    sync.RWMutex
	count map[string]int
}

func (c *SafeCounter) Increment(key string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.count[key]++
}

func (c *SafeCounter) Get(key string) int {
	c.mu.RLock()  // multiple readers allowed simultaneously
	defer c.mu.RUnlock()
	return c.count[key]
}

// sync.Once — run initialization exactly once
var (
	dbOnce     sync.Once
	dbInstance *Database
)

func GetDB() *Database {
	dbOnce.Do(func() {
		dbInstance = &Database{} // initialized exactly once, even concurrent calls
	})
	return dbInstance
}

// Atomic operations — lockless for simple counters
var requestCount int64

func handleRequest() {
	atomic.AddInt64(&requestCount, 1)
	// Process request...
}

func getRequestCount() int64 {
	return atomic.LoadInt64(&requestCount)
}

errgroup — Erros Simultâneos

package main

import (
	"context"
	"fmt"

	"golang.org/x/sync/errgroup"
)

func main() {
	ctx := context.Background()

	// Run goroutines, collect first error
	g, ctx := errgroup.WithContext(ctx)

	var user, posts, stats interface{}

	g.Go(func() error {
		var err error
		user, err = fetchUser(ctx, 1)
		return err
	})

	g.Go(func() error {
		var err error
		posts, err = fetchPosts(ctx, 1)
		return err
	})

	g.Go(func() error {
		var err error
		stats, err = fetchStats(ctx, 1)
		return err
	})

	// Wait for all goroutines
	if err := g.Wait(); err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println(user, posts, stats)
}

A simultaneidade em 2026 é elegante e poderosa quando você segue o mantra: “compartilhe memória comunicando-se, não comunique compartilhando memória”. Use goroutines livremente (elas custam aproximadamente 2 KB cada), coordene com canais e WaitGroups, lide com cancelamento de contexto em todas as operações de longa duração e evite estado mutável compartilhado (prefira canais ou operações atômicas).

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা