Introduction
In Rust, AtomicUsize is an atomic integer type that provides thread-safe operations on the platform's native pointer-sized unsigned integers. It is part of the standard library's std::sync::atomic module and is useful for shared state between threads where you need to perform atomic operations without using locks.
Using AtomicUsize
To use AtomicUsize, you need to import it from the std::sync::atomic module. You can create an AtomicUsize by using the AtomicUsize::new method. Atomic operations such as store, load, fetch_add, and compare_and_swap can be used to manipulate the value safely across threads.
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(5);
println!("Initial value: {}", atomic.load(Ordering::Relaxed));
atomic.store(10, Ordering::Relaxed);
println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Initial value: 5
// Updated value: 10
Key Methods
Below are some of the key methods exposed by the AtomicUSize class:
AtomicUsize::new
Creates a new AtomicUsize with the given initial value.
use std::sync::atomic::AtomicUsize;
fn main() {
let atomic = AtomicUsize::new(10);
println!("Created AtomicUsize with initial value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Created AtomicUsize with initial value: 10
load
Returns the current value of the AtomicUsize.
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(20);
println!("Current value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Current value: 20
store
Sets the value of the AtomicUsize.
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(30);
atomic.store(40, Ordering::Relaxed);
println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Updated value: 40
compare_and_swap
Compares the current value with current, and if they are equal, sets it to new.
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(50);
let old_value = atomic.compare_and_swap(50, 60, Ordering::Relaxed);
println!("Old value: {}, New value: {}", old_value, atomic.load(Ordering::Relaxed));
}
// Output:
// Old value: 50
// New value: 60
fetch_add
Adds to the current value, returning the previous value.
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(70);
let previous = atomic.fetch_add(10, Ordering::Relaxed);
println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 70
// New value: 80
fetch_sub
Subtracts from the current value, returning the previous value.
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(100);
let previous = atomic.fetch_sub(20, Ordering::Relaxed);
println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 100
// New value: 80
Example Usage
Example 1: Basic Usage
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(5);
println!("Initial value: {}", atomic.load(Ordering::Relaxed));
atomic.store(10, Ordering::Relaxed);
println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Initial value: 5
// Updated value: 10
Example 2: Using compare_and_swap
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(50);
let old_value = atomic.compare_and_swap(50, 60, Ordering::Relaxed);
println!("Old value: {}, New value: {}", old_value, atomic.load(Ordering::Relaxed));
}
// Output:
// Old value: 50
// New value: 60
Example 3: Using fetch_add
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(70);
let previous = atomic.fetch_add(10, Ordering::Relaxed);
println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 70
// New value: 80
Example 4: Using fetch_sub
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let atomic = AtomicUsize::new(100);
let previous = atomic.fetch_sub(20, Ordering::Relaxed);
println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 100
// New value: 80
Example 5: Using with Multiple Threads
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;
fn main() {
let atomic = Arc::new(AtomicUsize::new(0));
let atomic1 = Arc::clone(&atomic);
let handle1 = thread::spawn(move || {
atomic1.fetch_add(1, Ordering::Relaxed);
});
let atomic2 = Arc::clone(&atomic);
let handle2 = thread::spawn(move || {
atomic2.fetch_add(1, Ordering::Relaxed);
});
handle1.join().unwrap();
handle2.join().unwrap();
println!("Final value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Final value: 2
Considerations
- Operations on
AtomicUsizeare lock-free and thread-safe. - Always use the appropriate
Orderingwhen performing atomic operations to ensure the correct memory ordering guarantees. - Use
AtomicUsizewhen you need to share a simple integer state across threads without the overhead of using a mutex.
