Atomics

Standard Atomic Types

std cat-concurrency

Atomic types in std::sync::atomic⮳ provide primitive shared-memory communication between threads, and are the building blocks of other concurrent types.

The atomic module provides std::sync::atomic::AtomicBool⮳, std::sync::atomic::AtomicIsize⮳, std::sync::atomic::AtomicUsize⮳, std::sync::atomic::AtomicI8⮳, std::sync::atomic::AtomicU16⮳, etc.

//! Demonstrates the use of atomic types.
//!
//! This example uses `AtomicUsize` to maintain a global counter of threads.
//! Atomic operations ensure that modifications to the counter are
//! thread-safe.
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;

// Declare a static atomic counter.
// Atomic variables are safe to share between threads (they implement `Sync`).
static GLOBAL_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);

fn main() {
    // `fetch_add` atomically increments the counter and returns the previous
    // value.
    // Each atomic access takes an `Ordering`. `Ordering::SeqCst` ensures
    // sequential consistency, which is the strongest, safest, but slowest
    // memory ordering. Technically, a weaker `Ordering` would work here.
    // Read more about Ordering in the [Nomicon](https://doc.rust-lang.org/nomicon/atomics.html).
    let old_thread_count = GLOBAL_THREAD_COUNT.fetch_add(1, Ordering::SeqCst);
    println!("Threads: {}", old_thread_count + 1);

    // Create multiple threads.
    std::thread::scope(|s| {
        for _ in 0..5 {
            s.spawn(|| {
                // Increment the counter for each new thread.
                GLOBAL_THREAD_COUNT.fetch_add(1, Ordering::SeqCst);
            });
        }
    });
    // The scope guarantees all threads will be joined at the end of the scope.

    // Get the final thread count.
    let final_thread_count = GLOBAL_THREAD_COUNT.load(Ordering::SeqCst);
    println!("Final threads: {}", final_thread_count);
    assert_eq!(final_thread_count, 6);
}

The most common way to share an atomic variable is to put it into an std::sync::Arc⮳ (an atomically-reference-counted shared pointer).

AtomicCell with crossbeam

crossbeam-utils-website crossbeam-utils crossbeam-utils-crates.io crossbeam-utils-github crossbeam-utils-lib.rs cat-algorithms cat-concurrency cat-data-structures cat-no-std

crossbeam_utilsprovides miscellaneous tools for concurrent programming. It offers crossbeam::atomic::AtomicCell⮳, a thread-safe mutable memory location. This type is equivalent to std::cell::Cell⮳, except it can also be shared among multiple threads. Operations on AtomicCells use atomic instructions whenever possible, and synchronize using global locks otherwise.

use crossbeam_utils::atomic::AtomicCell;

/// Demonstrates the use of `AtomicCell` from the `crossbeam-utils` crate.
fn main() {
    // Create a new AtomicCell with an initial value of 7.
    let a = AtomicCell::new(7);

    // Loads a value from the atomic cell.
    assert_eq!(a.load(), 7);
    // Stores a value into the atomic cell.
    a.store(8);
    assert_eq!(a.load(), 8);
    // Stores val into the atomic cell and returns the previous value.
    assert_eq!(a.swap(9), 8);
    assert_eq!(a.load(), 9);

    // Extract the inner value from the AtomicCell.
    // Consumes the atomic and returns the contained value.
    let v = a.into_inner();
    assert_eq!(v, 9);
    println!("{}", v);
}

arc-swap

arc-swap arc-swap-crates.io arc-swap-github arc-swap-lib.rs cat-data-structures cat-memory-management

The ArcSwap type in arc-swap is a container for an Arc that can be changed atomically. Semantically, it is similar to Atomic<Arc<T>> (if there was such a thing) or RwLock<Arc<T>> (but without the need for the locking). It is optimized for read-mostly scenarios, with consistent performance characteristics.