On this page
Threads and Synchronization Primitives
The concurrency toolbox of the Rust standard library—Mutex/RwLock provide shared mutable access, mpsc channels pass messages between threads (without sharing memory), Condvar handles condition waiting, and atomics and Ordering form the foundation of lock-free concurrency. Each tool corresponds to a pattern of "passing information between threads."
std::thread
let handle = spawn;
handle.join.unwrap; // Wait for the thread to finish
Each thread has its own stack (2MB by default on Linux, adjustable via Builder::stack_size). The closure passed to std::thread::spawn must be Send + 'static—the thread may outlive the spawner.
Mutex: Mutex Lock
use Mutex;
let m = new;
* m.lock.unwrap += 1; // lock() returns a MutexGuard
// MutexGuard drop → automatically unlocks (even in case of panic, unwinding will drop)
MutexGuard<'_, T> implements DerefMut (giving you &mut T) and Drop (releasing the lock). There is no need to manually call unlock(). If the thread holding the lock panics, the Mutex becomes "poisoned"—subsequent calls to lock() return Err(PoisonError), alerting you that the data might be in an inconsistent state.
RwLock: Read-Write Lock
use RwLock;
let lock = new;
let r1 = lock.read.unwrap; // Shared read lock
let r2 = lock.read.unwrap; // Multiple readers can coexist
// let w = lock.write().unwrap(); // WOULD BLOCK: readers are active
drop; drop; // readers gone
let mut w = lock.write.unwrap; // Now writing is allowed
RwLock is suitable for scenarios with many reads and few writes. However, be aware: if readers are continuous, writers may starve. std::sync::RwLock does not guarantee fairness (it is not FIFO)—specific behavior depends on the OS scheduler.
mpsc: Multi-Producer Single-Consumer Channel
use mpsc;
let = channel;
spawn;
let msg = rx.recv.unwrap; // Block waiting for a message
recv() blocks the current thread until a message arrives—this is not async. For async scenarios, use tokio::sync::mpsc. When all tx senders are dropped, rx.recv() returns Err(RecvError)—this is important: the "closing" of a channel is implicitly communicated via the Drop of the sender.
Condvar: Condition Variable
use ;
let pair = new;
// Waiting thread:
let = &*pair;
let mut guard = lock.lock.unwrap;
while !*guard
// Notifying thread:
let = &*pair;
*lock.lock.unwrap = true;
cvar.notify_one;
You must use a while loop instead of an if statement to check the condition—spurious wakeups can occur (the OS might wake up the thread even if the condition is not met).
Atomic and Ordering
use ;
static COUNTER: AtomicUsize = new;
COUNTER.fetch_add;
Relaxed: Only guarantees atomicity, not the order relative to surrounding memory operations—Use case: simple counting, no need to synchronize with other variables. Acquire/Release: One-way synchronization—Acquire ensures that subsequent reads see writes that happened before Release. Use case: implementing locks. SeqCst: Strongest—globally consistent ordering. Use case: default choice, unless you know why you need a weaker ordering.
References
- Rust Book: Chapter 16
- Rustonomicon: Atomics, memory ordering
- CppReference: memory_order (the definition in C++ also applies to Rust's atomic model)
Keywords: thread, Mutex, RwLock, mpsc, channel, Condvar, AtomicUsize, Ordering, SeqCst, Acquire, Release