std.Thread — Threads Nativas
O módulo std.Thread fornece acesso a threads nativas do sistema operacional no Zig. Ele permite criar threads, aguardar sua conclusão, e trabalhar com primitivas de sincronização como mutexes e condições. O modelo de threading do Zig é direto e explícito — sem runtime oculto, sem coloração de funções (async/sync) e sem garbage collector interferindo.
Visão Geral
const std = @import("std");
const Thread = std.Thread;
Ciclo de Vida de uma Thread
- Spawn: Cria e inicia a thread com uma função e argumentos
- Execução: A thread roda concorrentemente
- Join: O chamador espera a thread terminar e coleta o resultado
- Detach: Alternativa ao join — a thread roda independentemente
Funções Principais
Criação e Controle
// Cria e inicia uma nova thread
pub fn spawn(config: SpawnConfig, function: anytype, args: anytype) !Thread
// Aguarda a thread terminar e retorna o resultado
pub fn join(self: Thread) ReturnType
// Desanexa a thread (roda independentemente)
pub fn detach(self: Thread) void
// Retorna o ID da thread atual
pub fn getCurrentId() Id
SpawnConfig
pub const SpawnConfig = struct {
// Tamanho da stack (null = padrão do SO)
stack_size: ?usize = null,
};
Exemplo 1: Criação e Join de Threads
const std = @import("std");
fn trabalho(id: usize) void {
std.debug.print("Thread {d}: iniciando trabalho\n", .{id});
// Simula trabalho computacional
var soma: u64 = 0;
for (0..1_000_000) |i| {
soma +%= i;
}
std.debug.print("Thread {d}: resultado = {d}\n", .{ id, soma });
}
pub fn main() !void {
const NUM_THREADS = 4;
var threads: [NUM_THREADS]std.Thread = undefined;
// Spawn das threads
for (0..NUM_THREADS) |i| {
threads[i] = try std.Thread.spawn(.{}, trabalho, .{i});
}
std.debug.print("Main: todas as threads iniciadas\n", .{});
// Join — espera todas terminarem
for (threads) |t| {
t.join();
}
std.debug.print("Main: todas as threads terminaram\n", .{});
}
Exemplo 2: Threads com Retorno de Valor
const std = @import("std");
fn calcularFatorial(n: u64) u64 {
var resultado: u64 = 1;
var i: u64 = 2;
while (i <= n) : (i += 1) {
resultado *%= i;
}
return resultado;
}
fn threadFatorial(n: u64) u64 {
std.debug.print("Calculando {d}!...\n", .{n});
return calcularFatorial(n);
}
pub fn main() !void {
// Cria threads para cálculos paralelos
const t1 = try std.Thread.spawn(.{}, threadFatorial, .{@as(u64, 10)});
const t2 = try std.Thread.spawn(.{}, threadFatorial, .{@as(u64, 15)});
const t3 = try std.Thread.spawn(.{}, threadFatorial, .{@as(u64, 20)});
// Join coleta os resultados
const r1 = t1.join();
const r2 = t2.join();
const r3 = t3.join();
const stdout = std.io.getStdOut().writer();
try stdout.print("10! = {d}\n", .{r1});
try stdout.print("15! = {d}\n", .{r2});
try stdout.print("20! = {d}\n", .{r3});
}
Exemplo 3: Processamento Paralelo de Dados
const std = @import("std");
const Resultado = struct {
soma: u64,
min: i32,
max: i32,
};
fn processarBloco(dados: []const i32) Resultado {
var soma: u64 = 0;
var min_val: i32 = std.math.maxInt(i32);
var max_val: i32 = std.math.minInt(i32);
for (dados) |v| {
soma += @as(u64, @intCast(@abs(v)));
if (v < min_val) min_val = v;
if (v > max_val) max_val = v;
}
return .{ .soma = soma, .min = min_val, .max = max_val };
}
pub fn main() !void {
// Dados de exemplo
var dados: [10000]i32 = undefined;
for (&dados, 0..) |*d, i| {
d.* = @intCast(@as(i64, @intCast(i)) - 5000);
}
const NUM_THREADS = 4;
const bloco_tam = dados.len / NUM_THREADS;
var threads: [NUM_THREADS]std.Thread = undefined;
// Divide o trabalho
for (0..NUM_THREADS) |i| {
const inicio = i * bloco_tam;
const fim = if (i == NUM_THREADS - 1) dados.len else (i + 1) * bloco_tam;
const bloco = dados[inicio..fim];
threads[i] = try std.Thread.spawn(.{}, processarBloco, .{bloco});
}
// Coleta resultados
var soma_total: u64 = 0;
var min_global: i32 = std.math.maxInt(i32);
var max_global: i32 = std.math.minInt(i32);
for (threads) |t| {
const r = t.join();
soma_total += r.soma;
if (r.min < min_global) min_global = r.min;
if (r.max > max_global) max_global = r.max;
}
const stdout = std.io.getStdOut().writer();
try stdout.print("Resultados ({d} threads):\n", .{NUM_THREADS});
try stdout.print(" Soma absoluta: {d}\n", .{soma_total});
try stdout.print(" Mínimo: {d}\n", .{min_global});
try stdout.print(" Máximo: {d}\n", .{max_global});
}
Padrões Comuns
Thread com Estado Compartilhado
Para compartilhar dados entre threads, use ponteiros e sincronização:
var contador = std.atomic.Value(u64).init(0);
fn incrementar() void {
for (0..1000) |_| {
_ = contador.fetchAdd(1, .seq_cst);
}
}
Detach vs Join
join(): Bloqueia até a thread terminar; pode capturar o valor de retornodetach(): A thread continua em background; não é possível obter o resultado
Módulos Relacionados
- std.Thread.Mutex — Exclusão mútua
- std.Thread.Pool — Pool de threads
- std.atomic — Operações atômicas
- std.event — Event loop