---
title: "Concorrência em Zig: Threads, Channels e Padrões de Paralelismo"
url: "https://ziglang.com.br/tutoriais/concorrencia-em-zig/"
markdown_url: "https://ziglang.com.br/tutoriais/concorrencia-em-zig.MD"
description: "Tutorial completo de concorrência em Zig. Aprenda a trabalhar com threads std.Thread, channels, mutex, thread pools e async/await para programas paralelos de alta performance."
date: "2026-02-10"
author: ""
---

# Concorrência em Zig: Threads, Channels e Padrões de Paralelismo

Tutorial completo de concorrência em Zig. Aprenda a trabalhar com threads std.Thread, channels, mutex, thread pools e async/await para programas paralelos de alta performance.


Concorrência é um dos tópicos mais procurados por desenvolvedores Zig, e por um bom motivo: **Zig oferece controle total sobre paralelismo**, sem a complexidade de Rust nem as restrições de Go. Neste tutorial completo, você vai dominar todos os padrões de concorrência em Zig: desde threads básicas até sistemas de worker pools de produção.

> **⚠️ Aviso:** A API de `async/await` do Zig está em evolução. Este tutorial foca nos recursos **estáveis**: `std.Thread`, channels e thread pools. Para async, consulte nosso [guia dedicado de async/await](/tutoriais/async-await-zig/).

## Índice

1. [Modelo de Concorrência do Zig](#modelo-de-concorrência-do-zig)
2. [Threads em Zig: O Básico](#threads-em-zig-o-básico)
3. [Sincronização: Mutex e Condition](#sincronização-mutex-e-condition)
4. [Channels: Comunicação Entre Threads](#channels-comunicação-entre-threads)
5. [Thread Pools: Paralelismo Eficiente](#thread-pools-paralelismo-eficiente)
6. [Padrões de Concorrência Práticos](#padrões-de-concorrência-práticos)
7. [Performance e Benchmarks](#performance-e-benchmarks)
8. [Armadilhas Comuns e Como Evitar](#armadilhas-comuns-e-como-evitar)
9. [Exercícios Práticos](#exercícios-práticos)
10. [FAQ](#faq)
11. [Próximos Passos](#próximos-passos)

---

## Modelo de Concorrência do Zig

Antes de escrever código, é importante entender como Zig aborda concorrência:

### Zig vs Outras Linguagens

| Aspecto | Zig | Go | Rust | C++ |
|---------|-----|-----|------|-----|
| **Modelo** | Threads explícitas + Async opt-in | Goroutines (green threads) | Ownership + borrowing | std::thread + futures |
| **Overhead** | Zero (OS threads) | Baixo (~2KB/goroutine) | Zero | Zero |
| **Controle** | Total | Alto | Alto | Alto |
| **Segurança** | Manual (com asserts) | GC garante | Compilador garante | Manual |
| **Curva de aprendizado** | Média | Baixa | Alta | Alta |

### Princípios do Zig para Concorrência

1. **Sem runtime**: Zig não tem scheduler embutido — você controla tudo
2. **Zero overhead**: Sem garbage collector, sem green threads
3. **Composição explícita**: Você escolhe o nível de abstração
4. **Erros tratáveis**: Falhas de alocação são `try`/`catch`, não panic

### Quando Usar Cada Abordagem

| Cenário | Abordagem Recomendada | Por quê? |
|---------|----------------------|----------|
| I/O-bound (rede, disco) | Async/await | Menos threads, mais conexões |
| CPU-bound (cálculos) | Thread pool | Usa todos os cores |
| Coordenação simples | Channels | Padrão produtor/consumidor claro |
| Estado compartilhado | Mutex + cond | Controle fino de acesso |
| High-performance server | I/O uring + async | Máxima eficiência |

---

## Threads em Zig: O Básico

A forma mais fundamental de paralelismo em Zig é através de `std.Thread`.

### Criando uma Thread

```zig
const std = @import("std");

pub fn main() !void {
    // Alocador para a thread
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Criar uma thread
    const thread = try std.Thread.spawn(
        .{},              // Configuração padrão
        workerFunction,  // Função a executar
        .{42},           // Argumentos (tuple)
    );

    // Aguardar a thread terminar
    thread.join();

    std.debug.print("Thread completada!\n", .{});
}

fn workerFunction(id: usize) void {
    std.debug.print("Worker {} executando\n", .{id});
    std.time.sleep(1 * std.time.ns_per_s); // Simula trabalho
    std.debug.print("Worker {} terminou\n", .{id});
}
```

**Saída:**
```
Worker 42 executando
Worker 42 terminou
Thread completada!
```

### Múltiplas Threads

```zig
pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Criar múltiplas threads
    const num_threads = 4;
    var threads: [num_threads]std.Thread = undefined;

    for (0..num_threads) |i| {
        threads[i] = try std.Thread.spawn(
            .{},
            workerFunction,
            .{i},
        );
    }

    // Aguardar todas
    for (threads) |thread| {
        thread.join();
    }

    std.debug.print("Todas as {d} threads completadas!\n", .{num_threads});
}
```

### Passando Dados para Threads

```zig
const WorkerData = struct {
    id: usize,
    name: []const u8,
    iterations: u32,
};

pub fn main() !void {
    const data = WorkerData{
        .id = 1,
        .name = "Processador de Imagens",
        .iterations = 1000,
    };

    const thread = try std.Thread.spawn(
        .{},
        processWorker,
        .{data},  // Passa por valor (copia)
    );

    thread.join();
}

fn processWorker(data: WorkerData) void {
    std.debug.print(
        "Worker {d} ({s}): processando {d} itens\n",
        .{data.id, data.name, data.iterations},
    );
}
```

### Retornando Resultados (com Channels)

Threads não retornam valores diretamente. Use channels (veja próxima seção) ou estado compartilhado:

```zig
const Result = struct {
    id: usize,
    value: i32,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Array compartilhado para resultados
    var results = [_]Result{undefined} ** 4;

    var threads: [4]std.Thread = undefined;
    for (0..4) |i| {
        // Passar ponteiro para resultado específico
        threads[i] = try std.Thread.spawn(
            .{},
            computeAndStore,
            .{ i, &results[i] },
        );
    }

    for (threads) |t| t.join();

    // Ler resultados
    for (results) |r| {
        std.debug.print("Resultado {d}: {d}\n", .{r.id, r.value});
    }
}

fn computeAndStore(id: usize, result: *Result) void {
    // Simula cálculo
    const value = @intCast(i32, id * 10);
    result.* = .{ .id = id, .value = value };
}
```

**Saída:**
```
Resultado 0: 0
Resultado 1: 10
Resultado 2: 20
Resultado 3: 30
```

---

## Sincronização: Mutex e Condition

Quando múltiplas threads acessam dados compartilhados, precisamos sincronização.

### Mutex Básico

```zig
const Counter = struct {
    mutex: std.Thread.Mutex,
    value: u64,

    fn init() Counter {
        return .{
            .mutex = .{},
            .value = 0,
        };
    }

    fn increment(self: *Counter) void {
        self.mutex.lock();
        defer self.mutex.unlock();
        self.value += 1;
    }

    fn get(self: *Counter) u64 {
        self.mutex.lock();
        defer self.mutex.unlock();
        return self.value;
    }
};

pub fn main() !void {
    var counter = Counter.init();

    var threads: [10]std.Thread = undefined;
    for (&threads) |*t| {
        t.* = try std.Thread.spawn(
            .{},
            incrementWorker,
            .{&counter},
        );
    }

    for (threads) |t| t.join();

    std.debug.print("Contador final: {d}\n", .{counter.get()});
    // Sempre imprime: Contador final: 10
}

fn incrementWorker(counter: *Counter) void {
    for (0..1000) |_| {
        counter.increment();
    }
}
```

### RwLock: Leitores e Escritores

Use `std.Thread.RwLock` quando tiver muitas leituras e poucas escritas:

```zig
const Cache = struct {
    rwlock: std.Thread.RwLock,
    data: std.StringHashMap(i32),

    fn get(self: *Cache, key: []const u8) ?i32 {
        self.rwlock.lockShared();  // Múltiplos leitores
        defer self.rwlock.unlockShared();
        return self.data.get(key);
    }

    fn put(self: *Cache, key: []const u8, value: i32) !void {
        self.rwlock.lock();  // Apenas um escritor
        defer self.rwlock.unlock();
        try self.data.put(key, value);
    }
};
```

### Condition Variables

Use `std.Thread.Condition` para sinalização entre threads:

```zig
const Queue = struct {
    mutex: std.Thread.Mutex,
    cond: std.Thread.Condition,
    items: std.ArrayList(i32),
    closed: bool,

    fn init(allocator: std.mem.Allocator) Queue {
        return .{
            .mutex = .{},
            .cond = .{},
            .items = std.ArrayList(i32).init(allocator),
            .closed = false,
        };
    }

    fn deinit(self: *Queue) void {
        self.items.deinit();
    }

    fn push(self: *Queue, item: i32) !void {
        self.mutex.lock();
        defer self.mutex.unlock();
        try self.items.append(item);
        self.cond.signal();  // Acorda um consumidor
    }

    fn pop(self: *Queue) ?i32 {
        self.mutex.lock();
        defer self.mutex.unlock();

        // Espera até ter item ou fila fechar
        while (self.items.items.len == 0 and !self.closed) {
            self.cond.wait(&self.mutex);
        }

        if (self.items.items.len == 0) return null;
        return self.items.orderedRemove(0);
    }

    fn close(self: *Queue) void {
        self.mutex.lock();
        defer self.mutex.unlock();
        self.closed = true;
        self.cond.broadcast();  // Acorda todos
    }
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var queue = Queue.init(allocator);
    defer queue.deinit();

    // Thread produtora
    const producer = try std.Thread.spawn(
        .{},
        producerFn,
        .{&queue},
    );

    // Thread consumidora
    const consumer = try std.Thread.spawn(
        .{},
        consumerFn,
        .{&queue},
    );

    producer.join();
    queue.close();  // Sinaliza fim
    consumer.join();
}

fn producerFn(queue: *Queue) !void {
    for (0..10) |i| {
        try queue.push(@intCast(i32, i));
        std.time.sleep(100 * std.time.ns_per_ms);
    }
}

fn consumerFn(queue: *Queue) void {
    while (queue.pop()) |item| {
        std.debug.print("Consumiu: {d}\n", .{item});
    }
    std.debug.print("Fila fechada\n", .{});
}
```

---

## Channels: Comunicação Entre Threads

Channels são a forma mais elegante de comunicação entre threads em Zig.

### Channel Básico

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Criar channel com buffer de 10 mensagens
    var channel = try std.Channel(i32).init(allocator, 10);
    defer channel.deinit();

    // Thread produtora
    const producer = try std.Thread.spawn(
        .{},
        sendNumbers,
        .{&channel},
    );

    // Thread consumidora
    const consumer = try std.Thread.spawn(
        .{},
        receiveNumbers,
        .{&channel},
    );

    producer.join();
    channel.close();  // Fecha para envio
    consumer.join();
}

fn sendNumbers(channel: *std.Channel(i32)) !void {
    for (0..5) |i| {
        try channel.send(@intCast(i32, i));
        std.debug.print("Enviou: {d}\n", .{i});
    }
}

fn receiveNumbers(channel: *std.Channel(i32)) void {
    while (channel.receive()) |num| {
        std.debug.print("Recebeu: {d}\n", .{num});
    }
}
```

**Saída:**
```
Enviou: 0
Enviou: 1
Recebeu: 0
Recebeu: 1
Enviou: 2
Enviou: 3
Recebeu: 2
...
```

### Select: Múltiplos Channels

```zig
fn multiplex(
    ch1: *std.Channel(i32),
    ch2: *std.Channel(i32),
    done: *std.Channel(void),
) !void {
    var buf1: i32 = undefined;
    var buf2: i32 = undefined;

    while (true) {
        // select é simulado com polling em Zig
        // (API nativa em desenvolvimento)
        
        if (ch1.tryReceive(&buf1)) |val| {
            std.debug.print("Ch1: {d}\n", .{val});
        } else if (ch2.tryReceive(&buf2)) |val| {
            std.debug.print("Ch2: {d}\n", .{val});
        } else if (done.tryReceive(null)) |_| {
            break;  // Sinal de término
        } else {
            // Pequena pausa para não busy-wait
            std.time.sleep(1 * std.time.ns_per_ms);
        }
    }
}
```

### Fan-Out/Fan-In Pattern

Distribuir trabalho entre workers e coletar resultados:

```zig
const Task = struct {
    id: usize,
    data: []const u8,
};

const Result = struct {
    task_id: usize,
    hash: u64,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const num_workers = 4;

    // Channels para distribuição e coleta
    var task_channel = try std.Channel(Task).init(allocator, 100);
    defer task_channel.deinit();

    var result_channel = try std.Channel(Result).init(allocator, 100);
    defer result_channel.deinit();

    // Iniciar workers
    var workers: [num_workers]std.Thread = undefined;
    for (&workers) |*w| {
        w.* = try std.Thread.spawn(
            .{},
            workerFn,
            .{&task_channel, &result_channel},
        );
    }

    // Enviar tasks (fan-out)
    for (0..20) |i| {
        const task = Task{
            .id = i,
            .data = "dados para processar",
        };
        try task_channel.send(task);
    }
    task_channel.close();

    // Coletar resultados (fan-in)
    var results_count: usize = 0;
    while (result_channel.receive()) |result| {
        std.debug.print("Resultado {d}: hash={d}\n", 
            .{result.task_id, result.hash});
        results_count += 1;
    }

    // Aguardar workers
    for (workers) |w| w.join();

    std.debug.print("\nTotal processado: {d}\n", .{results_count});
}

fn workerFn(
    tasks: *std.Channel(Task),
    results: *std.Channel(Result),
) !void {
    while (tasks.receive()) |task| {
        // Simula processamento
        const hash = @as(u64, task.id) * 31;
        
        try results.send(.{
            .task_id = task.id,
            .hash = hash,
        });
    }
}
```

---

## Thread Pools: Paralelismo Eficiente

Criar threads é caro. Thread pools reutilizam threads para melhor performance.

### Thread Pool Simples

```zig
const ThreadPool = struct {
    const Task = struct {
        runFn: *const fn (*anyopaque) void,
        context: *anyopaque,
    };

    allocator: std.mem.Allocator,
    threads: []std.Thread,
    queue: std.ArrayList(Task),
    mutex: std.Thread.Mutex,
    cond: std.Thread.Condition,
    shutdown: bool,

    fn init(allocator: std.mem.Allocator, num_threads: usize) !ThreadPool {
        var pool = ThreadPool{
            .allocator = allocator,
            .threads = try allocator.alloc(std.Thread, num_threads),
            .queue = std.ArrayList(Task).init(allocator),
            .mutex = .{},
            .cond = .{},
            .shutdown = false,
        };

        for (pool.threads) |*t| {
            t.* = try std.Thread.spawn(.{}, workerLoop, .{&pool});
        }

        return pool;
    }

    fn deinit(self: *ThreadPool) void {
        self.mutex.lock();
        self.shutdown = true;
        self.cond.broadcast();
        self.mutex.unlock();

        for (self.threads) |t| t.join();
        self.allocator.free(self.threads);
        self.queue.deinit();
    }

    fn submit(self: *ThreadPool, comptime T: type, context: *T, 
              comptime func: fn (*T) void) !void {
        const wrapper = struct {
            fn run(ptr: *anyopaque) void {
                func(@ptrCast(@alignCast(ptr)));
            }
        }.run;

        self.mutex.lock();
        defer self.mutex.unlock();
        
        try self.queue.append(.{
            .runFn = wrapper,
            .context = context,
        });
        self.cond.signal();
    }

    fn workerLoop(self: *ThreadPool) void {
        while (true) {
            self.mutex.lock();
            
            while (self.queue.items.len == 0 and !self.shutdown) {
                self.cond.wait(&self.mutex);
            }

            if (self.shutdown and self.queue.items.len == 0) {
                self.mutex.unlock();
                break;
            }

            const task = self.queue.orderedRemove(0);
            self.mutex.unlock();

            task.runFn(task.context);
        }
    }
};

// Uso
const WorkItem = struct {
    id: usize,
    result: i32,
};

fn processItem(item: *WorkItem) void {
    // Simula trabalho
    std.time.sleep(10 * std.time.ns_per_ms);
    item.result = @intCast(i32, item.id * item.id);
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var pool = try ThreadPool.init(allocator, 4);
    defer pool.deinit();

    var items: [10]WorkItem = undefined;
    for (&items, 0..) |*item, i| {
        item.id = i;
        try pool.submit(WorkItem, item, processItem);
    }

    // Aguardar (em produção, use um mecanismo de notificação)
    std.time.sleep(500 * std.time.ns_per_ms);

    for (items) |item| {
        std.debug.print("Item {d}: resultado = {d}\n", 
            .{item.id, item.result});
    }
}
```

### Thread Pool da Standard Library

Zig 0.12+ inclui `std.Thread.Pool`:

```zig
pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Criar pool com 4 threads
    var pool: std.Thread.Pool = undefined;
    try pool.init(.{
        .allocator = allocator,
        .n_jobs = 4,
    });
    defer pool.deinit();

    // Executar tarefas
    var wg = std.Thread.WaitGroup{};
    
    for (0..10) |i| {
        pool.spawnWg(&wg, workerFn, .{i});
    }

    // Aguardar todas
    wg.wait();
}

fn workerFn(id: usize) void {
    std.debug.print("Worker {d} executando\n", .{id});
    std.time.sleep(50 * std.time.ns_per_ms);
}
```

---

## Padrões de Concorrência Práticos

### 1. Pipeline

Processamento em estágios conectados por channels:

```zig
fn pipelineExample() !void {
    // Stage 1: Gerar números
    // Stage 2: Filtrar pares
    // Stage 3: Calcular quadrados
    // Stage 4: Somar
}
```

### 2. Worker Pool com Retry

```zig
const RetryableTask = struct {
    data: []const u8,
    max_retries: u32,
    current_retry: u32 = 0,
};

fn workerWithRetry(tasks: *std.Channel(RetryableTask)) void {
    while (tasks.receive()) |*task| {
        while (task.current_retry <= task.max_retries) : (task.current_retry += 1) {
            if (tryProcess(task.data)) {
                break;  // Sucesso
            }
            std.time.sleep(std.time.ns_per_ms * 100 * task.current_retry);
        }
    }
}

fn tryProcess(data: []const u8) bool {
    // Simula processamento que pode falhar
    return std.crypto.random.boolean();
}
```

### 3. Parallel Map

```zig
fn parallelMap(
    allocator: std.mem.Allocator,
    input: []const i32,
    comptime mapFn: fn (i32) i32,
) ![]i32 {
    var pool: std.Thread.Pool = undefined;
    try pool.init(.{ .allocator = allocator, .n_jobs = 4 });
    defer pool.deinit();

    var results = try allocator.alloc(i32, input.len);
    errdefer allocator.free(results);

    var wg = std.Thread.WaitGroup{};
    
    for (input, 0..) |item, i| {
        pool.spawnWg(&wg, struct {
            fn run(ctx: struct {
                item: i32,
                out: *i32,
                f: fn (i32) i32,
            }) void {
                ctx.out.* = ctx.f(ctx.item);
            }
        }.run, .{.{
            .item = item,
            .out = &results[i],
            .f = mapFn,
        }});
    }

    wg.wait();
    return results;
}

fn double(x: i32) i32 { return x * 2; }

// Uso
pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const input = &[_]i32{1, 2, 3, 4, 5};
    const results = try parallelMap(allocator, input, double);
    defer allocator.free(results);

    std.debug.print("Resultados: {any}\n", .{results});
    // [2, 4, 6, 8, 10]
}
```

---

## Performance e Benchmarks

### Comparativo de Overhead

| Operação | Latência aproximada | Quando usar |
|----------|---------------------|-------------|
| Spawn thread | ~10-100 μs | Longa duração |
| Mutex lock/unlock | ~20-50 ns | Seção crítica curta |
| Channel send (buffered) | ~50-100 ns | Comunicação thread-safe |
| Thread pool submit | ~200-500 ns | Tarefas frequentes |
| Context switch | ~1-10 μs | Evitar excesso |

### Otimizações

1. **Evite contenção**: Use múltiplos mutexes para diferentes dados
2. **Batch processing**: Processe múltiplos itens por vez
3. **Lock-free quando possível**: Use `std.atomic` para contadores simples
4. **Alinhamento de cache**: Estruture dados para evitar false sharing

### Benchmark Simples

```zig
const std = @import("std");

fn benchmarkParallelSum(allocator: std.mem.Allocator, n: usize) !u64 {
    var timer = try std.time.Timer.start();

    var pool: std.Thread.Pool = undefined;
    try pool.init(.{ .allocator = allocator, .n_jobs = 4 });
    defer pool.deinit();

    const chunk_size = n / 4;
    var partial_sums: [4]u64 = [1]u64{0} ** 4;
    var wg = std.Thread.WaitGroup{};

    for (0..4) |i| {
        pool.spawnWg(&wg, struct {
            fn run(ctx: struct {
                idx: usize,
                start: usize,
                end: usize,
                out: *u64,
            }) void {
                var sum: u64 = 0;
                for (ctx.start..ctx.end) |j| {
                    sum += j;
                }
                ctx.out.* = sum;
            }
        }.run, .{.{
            .idx = i,
            .start = i * chunk_size,
            .end = if (i == 3) n else (i + 1) * chunk_size,
            .out = &partial_sums[i],
        }});
    }

    wg.wait();

    var total: u64 = 0;
    for (partial_sums) |s| total += s;

    const elapsed = timer.read();
    std.debug.print("Paralelo: {d} ns\n", .{elapsed});

    return total;
}

fn benchmarkSequentialSum(n: usize) u64 {
    var timer = std.time.Timer.start() catch unreachable;

    var sum: u64 = 0;
    for (0..n) |i| sum += i;

    const elapsed = timer.read();
    std.debug.print("Sequencial: {d} ns\n", .{elapsed});

    return sum;
}

pub fn main() !void {
    const n = 10_000_000;

    std.debug.print("Somatório de 0 até {d}:\n\n", .{n});

    const seq_result = benchmarkSequentialSum(n);
    
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const par_result = try benchmarkParallelSum(gpa.allocator(), n);

    std.debug.print("\nResultados iguais: {}\n", .{seq_result == par_result});
}
```

---

## Armadilhas Comuns e Como Evitar

### 1. Data Race

```zig
// ❌ ERRADO: Data race
var counter: u64 = 0;

fn incrementUnsafe() void {
    for (0..1000) |_| {
        counter += 1;  // Não é atômico!
    }
}

// ✅ CORRETO: Use mutex
fn incrementSafe(mutex: *std.Thread.Mutex, counter: *u64) void {
    for (0..1000) |_| {
        mutex.lock();
        defer mutex.unlock();
        counter.* += 1;
    }
}
```

### 2. Deadlock

```zig
// ❌ ERRADO: Ordem inconsistente de locks
fn transferAtoB(a: *Account, b: *Account, amount: u64) void {
    a.mutex.lock();
    b.mutex.lock();  // Pode deadlocked se outra thread faz B->A
    // ...
}

// ✅ CORRETO: Ordem consistente
fn transferSafe(a: *Account, b: *Account, amount: u64) void {
    const first = if (@intFromPtr(a) < @intFromPtr(b)) a else b;
    const second = if (@intFromPtr(a) < @intFromPtr(b)) b else a;
    
    first.mutex.lock();
    defer first.mutex.unlock();
    second.mutex.lock();
    defer second.mutex.unlock();
    // ...
}
```

### 3. Uso após Free

```zig
// ❌ ERRADO: Ponteiro para stack
fn badThread() !std.Thread {
    var data: i32 = 42;
    return std.Thread.spawn(.{}, worker, .{&data});
}  // data é destruído aqui!

// ✅ CORRETO: Alocação no heap
fn goodThread(allocator: std.mem.Allocator) !std.Thread {
    const data = try allocator.create(i32);
    data.* = 42;
    return std.Thread.spawn(.{}, workerWithFree, .{data, allocator});
}

fn workerWithFree(data: *i32, allocator: std.mem.Allocator) void {
    std.debug.print("{d}\n", .{data.*});
    allocator.destroy(data);
}
```

### 4. Busy Waiting

```zig
// ❌ ERRADO: Consome 100% CPU
while (!ready) {}  // Busy wait

// ✅ CORRETO: Use condition variable
while (!ready) {
    cond.wait(&mutex);
}
```

---

## Exercícios Práticos

### Exercício 1: Produtor/Consumidor

Implemente um sistema onde uma thread produz números primos e outra os imprime.

<details>
<summary>Ver solução</summary>

```zig
const std = @import("std");

fn isPrime(n: u32) bool {
    if (n < 2) return false;
    var i: u32 = 2;
    while (i * i <= n) : (i += 1) {
        if (n % i == 0) return false;
    }
    return true;
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var channel = try std.Channel(u32).init(allocator, 10);
    defer channel.deinit();

    const producer = try std.Thread.spawn(.{}, struct {
        fn run(ch: *std.Channel(u32)) !void {
            var n: u32 = 2;
            while (n < 100) : (n += 1) {
                if (isPrime(n)) try ch.send(n);
            }
        }
    }.run, .{&channel});

    const consumer = try std.Thread.spawn(.{}, struct {
        fn run(ch: *std.Channel(u32)) void {
            while (ch.receive()) |prime| {
                std.debug.print("Primo: {d}\n", .{prime});
            }
        }
    }.run, .{&channel});

    producer.join();
    channel.close();
    consumer.join();
}
```

</details>

### Exercício 2: HTTP Download Paralelo

Baixe múltiplas URLs em paralelo usando um thread pool.

<details>
<summary>Ver solução</summary>

```zig
const std = @import("std");

const DownloadTask = struct {
    url: []const u8,
    content: []u8 = &.{},
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const urls = &[_][]const u8{
        "https://example.com/1",
        "https://example.com/2",
        "https://example.com/3",
    };

    var tasks = try allocator.alloc(DownloadTask, urls.len);
    defer allocator.free(tasks);

    for (urls, 0..) |url, i| {
        tasks[i] = .{ .url = url };
    }

    var pool: std.Thread.Pool = undefined;
    try pool.init(.{ .allocator = allocator, .n_jobs = 4 });
    defer pool.deinit();

    var wg = std.Thread.WaitGroup{};
    for (tasks) |*task| {
        pool.spawnWg(&wg, downloadWorker, .{task, allocator});
    }
    wg.wait();

    for (tasks) |task| {
        std.debug.print("{s}: {d} bytes\n", 
            .{task.url, task.content.len});
        allocator.free(task.content);
    }
}

fn downloadWorker(task: *DownloadTask, allocator: std.mem.Allocator) void {
    // Simula download
    std.time.sleep(100 * std.time.ns_per_ms);
    
    task.content = allocator.dupe(u8, "Conteúdo simulado") 
        catch &[_]u8{};
}
```

</details>

### Exercício 3: Rate Limiter

Implemente um rate limiter que permite N requisições por segundo.

<details>
<summary>Ver solução</summary>

```zig
const std = @import("std");

const RateLimiter = struct {
    mutex: std.Thread.Mutex,
    tokens: u32,
    last_update: i64,
    max_tokens: u32,
    refill_rate: u32, // tokens por segundo

    fn init(max: u32, rate: u32) RateLimiter {
        return .{
            .mutex = .{},
            .tokens = max,
            .last_update = std.time.milliTimestamp(),
            .max_tokens = max,
            .refill_rate = rate,
        };
    }

    fn tryAcquire(self: *RateLimiter) bool {
        self.mutex.lock();
        defer self.mutex.unlock();

        const now = std.time.milliTimestamp();
        const elapsed = @divTrunc(now - self.last_update, 1000);
        const refill = @min(
            self.max_tokens - self.tokens,
            @as(u32, @intCast(elapsed)) * self.refill_rate,
        );
        
        self.tokens += refill;
        self.last_update = now;

        if (self.tokens > 0) {
            self.tokens -= 1;
            return true;
        }
        return false;
    }
};

pub fn main() !void {
    var limiter = RateLimiter.init(5, 2); // 5 burst, 2/segundo

    for (0..10) |i| {
        if (limiter.tryAcquire()) {
            std.debug.print("Requisição {d}: permitida\n", .{i});
        } else {
            std.debug.print("Requisição {d}: rate limited\n", .{i});
        }
        std.time.sleep(200 * std.time.ns_per_ms);
    }
}
```

</details>

---

## FAQ

### Preciso usar `volatile` para variáveis compartilhadas?

Não em Zig. Use `std.atomic` para operações atômicas ou mutex para seções críticas. `volatile` em Zig é apenas para MMIO (Memory-Mapped I/O).

### Quantas threads devo criar?

- **CPU-bound**: Número de cores físicos
- **I/O-bound**: Pode ser maior (experimente 2x os cores)
- **Mix**: Profile e ajuste

### Go tem goroutines, Rust tem tokio. Qual o equivalente Zig?

Zig não tem um runtime padrão. Você pode:
1. Usar `std.Thread` para threads OS (mais simples)
2. Implementar green threads (avançado)
3. Usar `xev` ou `io_uring` para async
4. Usar bibliotecas de terceiros

### Como fazer cancelamento de tarefas?

Use um canal de "done" ou `std.atomic.Atomic(bool)`:

```zig
var should_stop = std.atomic.Atomic(bool).init(false);

// Na thread
while (!should_stop.load(.seq_cst)) {
    // trabalho...
}

// Para cancelar
should_stop.store(true, .seq_cst);
```

### Posso usar async/await com threads?

Sim! Veja nosso [guia de async/await](/tutoriais/async-await-zig/) para detalhes. Em resumo:

```zig
const frame = async someAsyncFunction();
// ... outro trabalho ...
const result = await frame;
```

### Threads são seguras em Zig?

Zig não tem borrow checker como Rust. Segurança é manual:
- Use `@compileError` para detectar erros em comptime
- Use assertions (`std.debug.assert`) para validar
- Use `ThreadSanitizer` em builds de debug

---

## Próximos Passos

Agora que você domina concorrência em Zig:

1. **🔧 [Sistema de Build do Zig](/tutoriais/zig-build-system/)** — Aprenda a configurar builds multi-thread
2. **⚡ [Async/Await em Zig](/tutoriais/async-await-zig/)** — Para I/O-bound concurrency
3. **🔒 [Tratamento de Erros](/tutoriais/tratamento-de-erros-em-zig/)** — Essencial para código concorrente robusto
4. **🌐 [Servidor HTTP](/tutoriais/zig-http-server/)** — Aplica prática de concorrência

Se você quer comparar modelos de concorrência, <a href="https://golang.com.br/artigos/goroutines-channels/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go utiliza goroutines e channels como primitivas leves de concorrência</a>, enquanto <a href="https://rustlang.com.br/artigos/rust-concorrencia/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust garante thread safety em tempo de compilação com Send e Sync</a>.

### Recursos Adicionais

- [Zig Language Reference — Concurrency](https://ziglang.org/documentation/master/#Concurrency)
- [Amdahl's Law Calculator](https://www.desmos.com/calculator/mdeytg4w1e) — Calcule speedup teórico
- [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html) — Detecta data races

---

*Gostou deste tutorial? Compartilhe com outros desenvolvedores e deixe suas dúvidas nos comentários!*
