---
title: "Tipos Atômicos em Rust: std::sync::atomic"
url: "https://rustlang.com.br/stdlib/atomics/"
markdown_url: "https://rustlang.com.br/stdlib/atomics.MD"
description: "Guia completo de tipos atômicos em Rust: AtomicBool, AtomicUsize, Ordering, compare_and_swap, fetch_add e memory ordering em português."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Tipos Atômicos em Rust: std::sync::atomic

Guia completo de tipos atômicos em Rust: AtomicBool, AtomicUsize, Ordering, compare_and_swap, fetch_add e memory ordering em português.


## O que faz e quando usar

O módulo `std::sync::atomic` fornece tipos que permitem operações **atômicas** — leituras e escritas que são indivisíveis do ponto de vista de outras threads. Diferentemente de um `Mutex`, operações atômicas não usam locks do sistema operacional; elas são implementadas diretamente em instruções do processador, tornando-as extremamente rápidas.

Use tipos atômicos quando:

- Você precisa compartilhar **valores simples** (bool, inteiros, ponteiros) entre threads.
- Quer um **contador** ou **flag** acessível por múltiplas threads sem o overhead de um Mutex.
- Está implementando estruturas de dados **lock-free**.
- Precisa de **spin locks**, contadores de referência ou flags de cancelamento.

Não use atômicos quando:

- Precisa proteger **dados complexos** (structs, vetores, strings) — use `Mutex` ou `RwLock`.
- Não entende as implicações de **memory ordering** — use `Mutex` que é mais simples e seguro.

---

## Tipos e Funções Principais

| Tipo | Descrição |
|---|---|
| `AtomicBool` | Booleano atômico |
| `AtomicI8/I16/I32/I64` | Inteiros com sinal atômicos |
| `AtomicU8/U16/U32/U64` | Inteiros sem sinal atômicos |
| `AtomicUsize` / `AtomicIsize` | Inteiros do tamanho de ponteiro atômicos |
| `AtomicPtr<T>` | Ponteiro atômico |

### Operações comuns

| Método | Descrição |
|---|---|
| `load(ordering)` | Lê o valor atomicamente |
| `store(val, ordering)` | Escreve o valor atomicamente |
| `swap(val, ordering)` | Troca o valor e retorna o anterior |
| `compare_exchange(cur, new, ok, fail)` | CAS: troca se o valor atual é `cur` |
| `fetch_add(val, ordering)` | Soma e retorna o valor anterior |
| `fetch_sub(val, ordering)` | Subtrai e retorna o valor anterior |
| `fetch_or(val, ordering)` | OR bit-a-bit e retorna o anterior |
| `fetch_and(val, ordering)` | AND bit-a-bit e retorna o anterior |

---

## Memory Ordering — Ordenação de Memória

Este é o conceito mais importante (e mais confuso) dos atômicos. O `Ordering` define **quais garantias de visibilidade** a operação oferece em relação a outras operações de memória.

```
  Mais restritivo (mais lento, mais garantias)
  ┌─────────────────────────────────────────┐
  │  SeqCst   — Ordem total entre threads   │
  │  AcqRel   — Acquire + Release juntos    │
  │  Release  — Publica dados para leitores │
  │  Acquire  — Consome dados publicados    │
  │  Relaxed  — Sem garantias de ordem      │
  └─────────────────────────────────────────┘
  Menos restritivo (mais rápido, menos garantias)
```

| Ordering | Quando usar |
|---|---|
| `Relaxed` | Contadores simples onde a ordem não importa |
| `Acquire` | Leitura que precisa ver os dados escritos antes de um `Release` |
| `Release` | Escrita que publica dados para quem fizer `Acquire` |
| `AcqRel` | Operações de leitura+escrita (como `compare_exchange`) |
| `SeqCst` | Quando precisa de ordem total consistente entre todas as threads |

**Regra de ouro:** se você não tem certeza, use `SeqCst`. É o mais lento, mas o mais seguro. Otimize depois se for necessário.

### Padrão Acquire/Release

```
  Thread A (escrita)               Thread B (leitura)
  ─────────────────               ─────────────────
  dados = 42;
  flag.store(true, Release);  --> flag.load(Acquire) == true
                                  // Agora dados == 42 é garantido!
```

A escrita com `Release` garante que **todas as escritas anteriores** são visíveis para quem ler com `Acquire`.

---

## Exemplos de Código

### AtomicBool — flag de cancelamento

```rust
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

fn main() {
    let cancelado = Arc::new(AtomicBool::new(false));

    // Worker thread
    let cancelado_clone = Arc::clone(&cancelado);
    let worker = thread::spawn(move || {
        let mut iteracoes = 0u64;
        while !cancelado_clone.load(Ordering::Relaxed) {
            // Simular trabalho
            iteracoes += 1;
            if iteracoes % 1_000_000 == 0 {
                println!("Worker: {} iterações...", iteracoes);
            }
        }
        println!("Worker cancelado após {} iterações", iteracoes);
        iteracoes
    });

    // Deixar o worker rodar por 100ms
    thread::sleep(Duration::from_millis(100));
    cancelado.store(true, Ordering::Relaxed);
    println!("Sinal de cancelamento enviado!");

    let total = worker.join().unwrap();
    println!("Total de iterações: {}", total);
}
```

### AtomicUsize — contador compartilhado

```rust
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;

fn main() {
    let contador = Arc::new(AtomicUsize::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let contador = Arc::clone(&contador);
        handles.push(thread::spawn(move || {
            for _ in 0..1_000 {
                // fetch_add retorna o valor ANTERIOR à soma
                contador.fetch_add(1, Ordering::Relaxed);
            }
        }));
    }

    for h in handles {
        h.join().unwrap();
    }

    // Sempre será 10.000 — operações atômicas são indivisíveis
    println!("Contador: {}", contador.load(Ordering::Relaxed));
}
```

### compare_exchange — CAS (Compare-And-Swap)

A operação CAS é a base de muitos algoritmos lock-free. Ela troca o valor **apenas se** o valor atual for o esperado:

```rust
use std::sync::atomic::{AtomicI32, Ordering};

fn main() {
    let valor = AtomicI32::new(5);

    // Tentar trocar: se for 5, mude para 10
    match valor.compare_exchange(5, 10, Ordering::SeqCst, Ordering::SeqCst) {
        Ok(anterior) => println!("Trocou! Valor anterior: {}", anterior),
        Err(atual) => println!("Não trocou. Valor atual: {}", atual),
    }

    // Tentar trocar: se for 5, mude para 20 (vai falhar, pois agora é 10)
    match valor.compare_exchange(5, 20, Ordering::SeqCst, Ordering::SeqCst) {
        Ok(anterior) => println!("Trocou! Anterior: {}", anterior),
        Err(atual) => println!("Não trocou. Atual: {}", atual),
    }

    println!("Valor final: {}", valor.load(Ordering::SeqCst));
}
```

### Spin lock simples com atômicos

```rust
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;

struct SpinLock {
    locked: AtomicBool,
}

impl SpinLock {
    fn new() -> Self {
        SpinLock {
            locked: AtomicBool::new(false),
        }
    }

    fn lock(&self) {
        // Tentar trocar false -> true repetidamente
        while self
            .locked
            .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
            .is_err()
        {
            // Spin: aguardar até o lock ser liberado
            // hint::spin_loop() otimiza o consumo de CPU
            std::hint::spin_loop();
        }
    }

    fn unlock(&self) {
        self.locked.store(false, Ordering::Release);
    }
}

fn main() {
    let lock = Arc::new(SpinLock::new());
    let contador = Arc::new(std::sync::atomic::AtomicI32::new(0));
    let mut handles = vec![];

    for _ in 0..4 {
        let lock = Arc::clone(&lock);
        let contador = Arc::clone(&contador);
        handles.push(thread::spawn(move || {
            for _ in 0..1_000 {
                lock.lock();
                // Seção crítica
                let val = contador.load(Ordering::Relaxed);
                contador.store(val + 1, Ordering::Relaxed);
                lock.unlock();
            }
        }));
    }

    for h in handles {
        h.join().unwrap();
    }

    println!("Contador: {}", contador.load(Ordering::SeqCst));
    // Sempre 4000
}
```

### Acquire/Release — publicando dados

```rust
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;

fn main() {
    let dados = Arc::new(std::sync::Mutex::new(0));
    let pronto = Arc::new(AtomicBool::new(false));

    let dados_clone = Arc::clone(&dados);
    let pronto_clone = Arc::clone(&pronto);

    // Produtor
    let produtor = thread::spawn(move || {
        // Preparar os dados
        *dados_clone.lock().unwrap() = 42;
        // Publicar com Release: garante que a escrita acima é visível
        pronto_clone.store(true, Ordering::Release);
    });

    // Consumidor
    let dados_clone = Arc::clone(&dados);
    let pronto_clone = Arc::clone(&pronto);
    let consumidor = thread::spawn(move || {
        // Esperar com Acquire: garante ver as escritas antes do Release
        while !pronto_clone.load(Ordering::Acquire) {
            std::hint::spin_loop();
        }
        // Agora é garantido que dados == 42
        let valor = *dados_clone.lock().unwrap();
        println!("Consumidor leu: {}", valor);
        assert_eq!(valor, 42);
    });

    produtor.join().unwrap();
    consumidor.join().unwrap();
}
```

### Gerador de IDs único

```rust
use std::sync::atomic::{AtomicU64, Ordering};

static PROXIMO_ID: AtomicU64 = AtomicU64::new(1);

fn gerar_id() -> u64 {
    PROXIMO_ID.fetch_add(1, Ordering::Relaxed)
}

fn main() {
    let mut handles = vec![];

    for _ in 0..4 {
        handles.push(std::thread::spawn(|| {
            let mut ids = Vec::new();
            for _ in 0..5 {
                ids.push(gerar_id());
            }
            ids
        }));
    }

    let mut todos_ids: Vec<u64> = handles
        .into_iter()
        .flat_map(|h| h.join().unwrap())
        .collect();

    todos_ids.sort();
    println!("IDs gerados: {:?}", todos_ids);
    // Todos serão únicos: [1, 2, 3, ..., 20]
    assert_eq!(todos_ids.len(), 20);
}
```

---

## Padrões Comuns e Anti-padrões

### Padrão: estatísticas atômicas

```rust
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;

struct Metricas {
    requisicoes: AtomicU64,
    erros: AtomicU64,
    bytes_processados: AtomicU64,
}

impl Metricas {
    fn new() -> Self {
        Metricas {
            requisicoes: AtomicU64::new(0),
            erros: AtomicU64::new(0),
            bytes_processados: AtomicU64::new(0),
        }
    }

    fn registrar_requisicao(&self, bytes: u64) {
        self.requisicoes.fetch_add(1, Ordering::Relaxed);
        self.bytes_processados.fetch_add(bytes, Ordering::Relaxed);
    }

    fn registrar_erro(&self) {
        self.erros.fetch_add(1, Ordering::Relaxed);
    }

    fn relatorio(&self) -> String {
        format!(
            "Requisições: {}, Erros: {}, Bytes: {}",
            self.requisicoes.load(Ordering::Relaxed),
            self.erros.load(Ordering::Relaxed),
            self.bytes_processados.load(Ordering::Relaxed),
        )
    }
}

fn main() {
    let metricas = Arc::new(Metricas::new());
    let mut handles = vec![];

    for _ in 0..4 {
        let metricas = Arc::clone(&metricas);
        handles.push(thread::spawn(move || {
            for i in 0..100 {
                metricas.registrar_requisicao(1024);
                if i % 10 == 0 {
                    metricas.registrar_erro();
                }
            }
        }));
    }

    for h in handles {
        h.join().unwrap();
    }

    println!("{}", metricas.relatorio());
}
```

### Anti-padrão: Ordering errado

```rust
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};

// ERRADO: usar Relaxed quando precisa de sincronização de dados
fn exemplo_errado() {
    let dados = AtomicI32::new(0);
    let pronto = AtomicBool::new(false);

    // Thread A
    dados.store(42, Ordering::Relaxed);
    pronto.store(true, Ordering::Relaxed); // ERRADO! Pode ser reordenado

    // Thread B pode ver pronto=true mas dados=0
    // porque Relaxed não garante ordem entre operações diferentes
}

// CORRETO: usar Release/Acquire
fn exemplo_correto() {
    let dados = AtomicI32::new(0);
    let pronto = AtomicBool::new(false);

    // Thread A
    dados.store(42, Ordering::Relaxed); // OK: será "empurrado" pelo Release abaixo
    pronto.store(true, Ordering::Release); // Garante que escritas anteriores são visíveis

    // Thread B
    if pronto.load(Ordering::Acquire) {
        // Agora dados.load() é garantido retornar 42
        let _ = dados.load(Ordering::Relaxed);
    }
}

fn main() {
    exemplo_errado();
    exemplo_correto();
}
```

---

## Garantias de Thread Safety

- Todos os tipos atômicos são `Send + Sync` — podem ser compartilhados livremente entre threads.
- Operações atômicas são **indivisíveis**: nenhuma thread vê um valor "pela metade".
- `Ordering` controla apenas a **visibilidade** e **reordenação** de outras operações de memória ao redor da operação atômica.
- Na prática em x86/x86_64, `Relaxed` e `SeqCst` têm performance similar para loads. Em ARM, a diferença é mais significativa.
- Atômicos **não** são um substituto para `Mutex` quando você precisa fazer múltiplas operações como uma transação.

---

## Veja Também

- [Mutex e RwLock em Rust](/stdlib/mutex/) — quando atômicos não bastam
- [Once, OnceLock e LazyLock](/stdlib/once-oncelock/) — inicialização global com atômicos
- [Send e Sync](/stdlib/send-sync/) — por que atômicos são Sync
- [Padrões de Thread Safety](/stdlib/thread-safety-patterns/) — quando usar cada primitiva
- Documentação oficial: [`std::sync::atomic`](https://doc.rust-lang.org/std/sync/atomic/)
