---
title: "Cheatsheet: Concorrência em Zig"
url: "https://ziglang.com.br/cheatsheets/cheatsheet-concorr%C3%AAncia-em-zig/"
markdown_url: "https://ziglang.com.br/cheatsheets/cheatsheet-concorr%C3%AAncia-em-zig.MD"
description: "Referência rápida para concorrência em Zig: threads, mutexes, atomics, pools de threads e padrões de programação concorrente. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Concorrência em Zig

Referência rápida para concorrência em Zig: threads, mutexes, atomics, pools de threads e padrões de programação concorrente. Guia completo em português.


# Cheatsheet: Concorrência em Zig

Zig oferece primitivas de concorrência de baixo nível na biblioteca padrão, dando controle direto sobre threads, mutexes e operações atômicas. Diferente de linguagens com runtimes pesados (Go, Erlang), Zig não tem um scheduler embutido — você trabalha diretamente com threads do sistema operacional, mantendo a filosofia de zero overhead e controle total.

## Criação de Threads

### Thread simples

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

fn tarefa(id: usize) void {
    std.debug.print("Thread {d} executando\n", .{id});
}

pub fn main() !void {
    // Criar e iniciar thread
    const thread = try std.Thread.spawn(.{}, tarefa, .{1});

    // Fazer algo na thread principal...
    std.debug.print("Thread principal\n", .{});

    // Esperar a thread terminar
    thread.join();
}
```

### Múltiplas threads

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

fn trabalho(id: usize) void {
    std.debug.print("Worker {d} iniciou\n", .{id});
    // simular trabalho
    std.time.sleep(100 * std.time.ns_per_ms);
    std.debug.print("Worker {d} terminou\n", .{id});
}

pub fn main() !void {
    const NUM_THREADS = 4;
    var threads: [NUM_THREADS]std.Thread = undefined;

    // Iniciar todas as threads
    for (0..NUM_THREADS) |i| {
        threads[i] = try std.Thread.spawn(.{}, trabalho, .{i});
    }

    // Esperar todas terminarem
    for (threads) |t| {
        t.join();
    }

    std.debug.print("Todas as threads terminaram\n", .{});
}
```

### Thread com retorno via ponteiro

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

fn calcular(resultado: *i64) void {
    var soma: i64 = 0;
    for (0..1000) |i| {
        soma += @intCast(i);
    }
    resultado.* = soma;
}

pub fn main() !void {
    var resultado: i64 = 0;

    const thread = try std.Thread.spawn(.{}, calcular, .{&resultado});
    thread.join();

    std.debug.print("Resultado: {d}\n", .{resultado});
}
```

## Mutex

### Mutex básico para proteção de dados

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

const ContadorSeguro = struct {
    valor: i64 = 0,
    mutex: std.Thread.Mutex = .{},

    fn incrementar(self: *ContadorSeguro) void {
        self.mutex.lock();
        defer self.mutex.unlock();
        self.valor += 1;
    }

    fn ler(self: *ContadorSeguro) i64 {
        self.mutex.lock();
        defer self.mutex.unlock();
        return self.valor;
    }
};

pub fn main() !void {
    var contador = ContadorSeguro{};
    const NUM_THREADS = 8;
    const INCREMENTOS = 10000;
    var threads: [NUM_THREADS]std.Thread = undefined;

    for (0..NUM_THREADS) |i| {
        threads[i] = try std.Thread.spawn(.{}, struct {
            fn run(c: *ContadorSeguro) void {
                for (0..INCREMENTOS) |_| {
                    c.incrementar();
                }
            }
        }.run, .{&contador});
    }

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

    std.debug.print("Contador: {d} (esperado: {d})\n", .{
        contador.ler(),
        NUM_THREADS * INCREMENTOS,
    });
}
```

## Operações Atômicas

Para operações simples em dados compartilhados, atômicos são mais eficientes que mutex:

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

pub fn main() !void {
    var contador = std.atomic.Value(i64).init(0);
    const NUM_THREADS = 8;
    var threads: [NUM_THREADS]std.Thread = undefined;

    for (0..NUM_THREADS) |i| {
        threads[i] = try std.Thread.spawn(.{}, struct {
            fn run(c: *std.atomic.Value(i64)) void {
                for (0..10000) |_| {
                    _ = c.fetchAdd(1, .seq_cst);
                }
            }
        }.run, .{&contador});
    }

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

    std.debug.print("Contador: {d}\n", .{contador.load(.seq_cst)});
}
```

### Operações atômicas disponíveis

| Operação | Descrição |
|----------|-----------|
| `load(order)` | Ler valor atomicamente |
| `store(val, order)` | Escrever valor atomicamente |
| `fetchAdd(val, order)` | Soma atômica, retorna valor anterior |
| `fetchSub(val, order)` | Subtração atômica |
| `fetchAnd(val, order)` | AND atômico |
| `fetchOr(val, order)` | OR atômico |
| `cmpxchgWeak(expected, new, succ_order, fail_order)` | Compare-and-swap fraco |
| `cmpxchgStrong(expected, new, succ_order, fail_order)` | Compare-and-swap forte |

### Ordering de memória

| Ordering | Descrição |
|----------|-----------|
| `.relaxed` | Sem garantia de ordem (mais rápido) |
| `.acquire` | Leituras após esta veem escritas anteriores |
| `.release` | Escritas antes desta são visíveis após acquire |
| `.acq_rel` | Acquire + Release combinados |
| `.seq_cst` | Mais forte — ordem total sequencial (mais seguro) |

## Thread Pool

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

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

    var pool: std.Thread.Pool = undefined;
    try pool.init(.{
        .allocator = gpa.allocator(),
        .n_jobs = 4, // número de threads no pool
    });
    defer pool.deinit();

    // Agendar trabalho no pool
    for (0..20) |i| {
        pool.spawn(struct {
            fn work(id: usize) void {
                std.debug.print("Tarefa {d} executando\n", .{id});
            }
        }.work, .{i});
    }

    // Pool é finalizado no defer, esperando todas as tarefas
}
```

## Condition Variables

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

const FilaSegura = struct {
    dados: [100]i32 = undefined,
    tamanho: usize = 0,
    mutex: std.Thread.Mutex = .{},
    nao_vazio: std.Thread.Condition = .{},
    nao_cheio: std.Thread.Condition = .{},

    fn inserir(self: *FilaSegura, valor: i32) void {
        self.mutex.lock();
        defer self.mutex.unlock();

        while (self.tamanho >= 100) {
            self.nao_cheio.wait(&self.mutex);
        }

        self.dados[self.tamanho] = valor;
        self.tamanho += 1;
        self.nao_vazio.signal();
    }

    fn remover(self: *FilaSegura) i32 {
        self.mutex.lock();
        defer self.mutex.unlock();

        while (self.tamanho == 0) {
            self.nao_vazio.wait(&self.mutex);
        }

        self.tamanho -= 1;
        const valor = self.dados[self.tamanho];
        self.nao_cheio.signal();
        return valor;
    }
};
```

## Padrões Comuns

### Dados por thread (sem compartilhamento)

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

fn processarParte(inicio: usize, fim: usize, resultado: *i64) void {
    var soma: i64 = 0;
    for (inicio..fim) |i| {
        soma += @intCast(i);
    }
    resultado.* = soma;
}

pub fn main() !void {
    const TOTAL = 1_000_000;
    const NUM_THREADS = 4;
    const PARTE = TOTAL / NUM_THREADS;

    var resultados: [NUM_THREADS]i64 = undefined;
    var threads: [NUM_THREADS]std.Thread = undefined;

    for (0..NUM_THREADS) |i| {
        threads[i] = try std.Thread.spawn(
            .{},
            processarParte,
            .{ i * PARTE, (i + 1) * PARTE, &resultados[i] },
        );
    }

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

    var total: i64 = 0;
    for (resultados) |r| total += r;

    std.debug.print("Soma total: {d}\n", .{total});
}
```

### Once — Inicialização única thread-safe

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

var recurso_global: ?*Recurso = null;
var once = std.once(inicializarRecurso);

fn inicializarRecurso() void {
    // Executado apenas uma vez, mesmo com múltiplas threads
    recurso_global = criarRecurso();
}

fn obterRecurso() *Recurso {
    once.call();
    return recurso_global.?;
}
```

## Temporizadores e Sleep

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

pub fn main() void {
    // Dormir por tempo específico
    std.time.sleep(1 * std.time.ns_per_s);    // 1 segundo
    std.time.sleep(500 * std.time.ns_per_ms);  // 500 milissegundos

    // Medir tempo
    var timer = std.time.Timer.start() catch unreachable;
    // ... operação ...
    const elapsed_ns = timer.read();
    std.debug.print("Tempo: {d}ms\n", .{elapsed_ns / std.time.ns_per_ms});

    // Timestamp
    const timestamp = std.time.timestamp();
    std.debug.print("Timestamp: {d}\n", .{timestamp});
}
```

## Erros Comuns

```zig
// ERRO: Acessar dados compartilhados sem proteção
// var dados: i32 = 0;
// Thread 1: dados += 1;  // data race!
// Thread 2: dados += 1;

// CORRETO: Usar mutex ou atômico
var mutex = std.Thread.Mutex{};
mutex.lock();
defer mutex.unlock();
// dados += 1;

// ERRO: Deadlock — dois mutexes em ordem diferente
// Thread 1: lock(A) -> lock(B)
// Thread 2: lock(B) -> lock(A)  // DEADLOCK!

// CORRETO: Sempre trancar na mesma ordem
```

Para uma visão comparativa de concorrência em linguagens de sistemas, <a href="https://golang.com.br/artigos/goroutines-channels/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go oferece goroutines e channels</a> como primitivas de alto nível, 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 segurança em concorrência via ownership e Send/Sync</a> — abordagens bem distintas do modelo manual de Zig.

## Veja Também

- [Allocators](/cheatsheets/allocators/) — Alocadores thread-safe
- [Error Handling](/cheatsheets/error-handling/) — Erros em contexto concorrente
- [Producer-Consumer Pattern](/padroes/producer-consumer/) — Padrão produtor-consumidor
- [Pool de Objetos](/padroes/pool-objetos/) — Pool thread-safe
- [FAQ Performance](/faq/faq-performance/) — Quando usar concorrência
