Atomics
Recipe | Crates | Categories |
---|---|---|
Standard Atomic Types | ||
AtomicCell with crossbeam | ||
arc-swap |
Standard Atomic Types
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
provides 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
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.