Introduction
In Rust, AtomicU32 is an atomic integer type that provides thread-safe operations on 32-bit 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 AtomicU32
To use AtomicU32, you need to import it from the std::sync::atomic module. You can create an AtomicU32 by using the AtomicU32::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::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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 AtomicU32 class:
AtomicU32::new
Creates a new AtomicU32 with the given initial value.
use std::sync::atomic::AtomicU32;
fn main() {
let atomic = AtomicU32::new(10);
println!("Created AtomicU32 with initial value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Created AtomicU32 with initial value: 10
load
Returns the current value of the AtomicU32.
use std::sync::atomic::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::new(20);
println!("Current value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Current value: 20
store
Sets the value of the AtomicU32.
use std::sync::atomic::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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::{AtomicU32, Ordering};
fn main() {
let atomic = AtomicU32::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::{AtomicU32, Ordering};
use std::sync::Arc;
use std::thread;
fn main() {
let atomic = Arc::new(AtomicU32::new(0));
let atomic1 = Arc::clone(&atomic);
let
<pre><code class="rust">
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
AtomicU32are lock-free and thread-safe. - Always use the appropriate
Orderingwhen performing atomic operations to ensure the correct memory ordering guarantees. - Use
AtomicU32when you need to share a simple integer state across threads without the overhead of using a mutex.
See Also
- AtomicUsize - An atomic integer type for shared state between threads.
- Mutex - A mutual exclusion primitive for protecting shared data.
- RwLock - A read-write lock allowing multiple readers or a single writer.
