Concurrency in Rust
Rust offers fearless concurrency with compile‑time guarantees. Use threads for parallel execution and message passing or shared state with synchronization.
Threads
use std::thread;
let handle = thread::spawn(|| {
2 + 2
});
let result = handle.join().unwrap();
Use move to transfer ownership into the thread.
Sharing data: Arc and Mutex
Arc<T> provides atomic reference counting for shared ownership across threads. Wrap mutable shared data with Mutex<T>.
use std::{sync::{Arc, Mutex}, thread};
let data = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..4 {
let shared = Arc::clone(&data);
handles.push(thread::spawn(move || {
let mut guard = shared.lock().unwrap();
*guard += 1;
}));
}
for h in handles { h.join().unwrap(); }
Avoid long‑held locks; keep critical sections small.
Channels (message passing)
use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || {
tx.send("hello").unwrap();
});
println!("{}", rx.recv().unwrap());
Cloning the sender yields multi‑producer channels.
Send and Sync
Send: safe to move to another threadSync:&Tcan be shared across threads Most primitives implement these auto traits when their fields do.
Atomics
For lock‑free counters and flags, use std::sync::atomic::{AtomicUsize, Ordering}. Prefer channels/mutexes unless you need atomics.
Patterns
- Prefer message passing to minimize shared mutable state
- Use
Arc<Mutex<T>>only when necessary; avoid nested locks and deadlocks - Consider crates like
crossbeamfor advanced concurrency
Summary
- Threads execute work in parallel; use
movefor ownership - Choose channels for communication;
Arc<Mutex<T>>for shared mutable state - Understand
Send/Syncand prefer simple designs