Introduction
The AtomicI32
class in Rust provides a way to perform atomic operations on a 32-bit integer. It is part of Rust's standard library and is typically used for safe concurrent programming, allowing multiple threads to manipulate shared data without introducing data races.
Using AtomicI32
The AtomicI32
class can be used to create a thread-safe integer that supports atomic operations. Here is a basic example:
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(5);
atomic_val.store(10, Ordering::SeqCst);
let value = atomic_val.load(Ordering::SeqCst);
println!("Value: {}", value);
}
// Output: Value: 10
Key Methods
Below are some of the key methods exposed by the AtomicI32
class:
new
Creates a new AtomicI32
initialized to the given value.
use std::sync::atomic::AtomicI32;
fn main() {
let atomic_val = AtomicI32::new(0);
println!("Initial value: {}", atomic_val.load(Ordering::SeqCst));
}
// Output: Initial value: 0
load
Loads a value from the AtomicI32
. Takes an Ordering
argument to specify the memory ordering of the operation.
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(1);
let value = atomic_val.load(Ordering::Relaxed);
println!("Loaded value: {}", value);
}
// Output: Loaded value: 1
store
Stores a value into the AtomicI32
. Takes an Ordering
argument to specify the memory ordering of the operation.
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(1);
atomic_val.store(2, Ordering::Relaxed);
println!("Stored value: {}", atomic_val.load(Ordering::Relaxed));
}
// Output: Stored value: 2
swap
Swaps the current value with a new value. Returns the previous value. Takes an Ordering
argument to specify the memory ordering of the operation.
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(1);
let old_value = atomic_val.swap(3, Ordering::SeqCst);
println!("Old value: {}, New value: {}", old_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Old value: 1, New value: 3
compare_and_swap
Compares the current value with a specified value and, if they are equal, sets the current value to a new value. Returns the previous value. This method is deprecated in favor of compare_exchange
.
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(1);
let old_value = atomic_val.compare_and_swap(1, 4, Ordering::SeqCst);
println!("Old value: {}, New value: {}", old_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Old value: 1, New value: 4
fetch_add
Adds to the current value, returning the previous value. Takes an Ordering
argument to specify the memory ordering of the operation.
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(1);
let prev_value = atomic_val.fetch_add(5, Ordering::SeqCst);
println!("Previous value: {}, Current value: {}", prev_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Previous value: 1, Current value: 6
fetch_sub
Subtracts from the current value, returning the previous value. Takes an Ordering
argument to specify the memory ordering of the operation.
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(10);
let prev_value = atomic_val.fetch_sub(3, Ordering::SeqCst);
println!("Previous value: {}, Current value: {}", prev_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Previous value: 10, Current value: 7
Example Usage
Example 1: Basic Initialization and Load
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(42);
let value = atomic_val.load(Ordering::SeqCst);
println!("Loaded value: {}", value);
}
// Output: Loaded value: 42
Example 2: Atomic Store
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(5);
atomic_val.store(10, Ordering::SeqCst);
println!("Stored value: {}", atomic_val.load(Ordering::SeqCst));
}
// Output: Stored value: 10
Example 3: Atomic Swap
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(1);
let old_value = atomic_val.swap(3, Ordering::SeqCst);
println!("Old value: {}, New value: {}", old_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Old value: 1, New value: 3
Example 4: Compare and Swap
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(1);
let old_value = atomic_val.compare_and_swap(1, 4, Ordering::SeqCst);
println!("Old value: {}, New value: {}", old_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Old value: 1, New value: 4
Example 5: Fetch Add
use std::sync::atomic::{AtomicI32, Ordering};
fn main() {
let atomic_val = AtomicI32::new(1);
let prev_value = atomic_val.fetch_add(5, Ordering::SeqCst);
println!("Previous value: {}, Current value: {}", prev_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Previous value: 1, Current value: 6
Considerations
AtomicI32
provides low-level atomic operations for concurrency. Proper use of memory ordering (e.g.,Ordering::Relaxed
,Ordering::SeqCst
) is essential to ensure correctness.- Using atomic operations can be complex and error-prone; consider higher-level synchronization primitives like mutexes if simpler concurrency management is needed.
See Also
- AtomicU32 - Similar to
AtomicI32
, but for unsigned 32-bit integers. - Mutex - A mutual exclusion primitive useful for protecting shared data.
- RwLock - A reader-writer lock for allowing concurrent reads or exclusive writes.
- Arc - An atomic reference-counted smart pointer for shared ownership across threads.