Mutex and Arc
| Mutex guards data | |
| Arc shares ownership | |
| Lock briefly | |
| Poisoning note |
Mutex guards data
A mutex wraps data and hands out one mutable guard at a time. Locking blocks until the guard is free, then returns access to the inner value. The counter below rises by one while the guard lives.
use std::sync::Mutex;
fn main() {
let count = Mutex::new(0);
*count.lock().unwrap() += 1;
println!("{:?}", count);
}
The star dereferences the guard to reach the stored integer. Guards release automatically when they drop at scope end. Rust ties the lock and the data together, so unguarded access cannot compile.
Arc shares ownership
Arc is the thread safe counter behind shared ownership across threads. Cloning it bumps the count so each thread owns a handle. The spawned worker below increments through its own clone while main keeps the original.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let count = Arc::new(Mutex::new(0));
let other = Arc::clone(&count);
let handle = thread::spawn(move || {
*other.lock().unwrap() += 1;
});
handle.join().unwrap();
println!("{:?}", count);
}
Arc alone shares only read only data, so Mutex inside adds mutation. Both pieces compose without extra traits or macros. This pair is the standard answer to shared mutable state.
Lock briefly
Holding a guard blocks every other thread that wants the same data. Short critical sections keep programs fast and deadlock free. Length below copies out of the guard in one statement, then drops it at once.
use std::sync::Mutex;
fn main() {
let count = Mutex::new(vec![1, 2, 3]);
let len = count.lock().unwrap().len();
println!("{}", len);
}
The temporary guard lives only for its statement and frees before printing. Never call back into locked code while holding a guard. Copy out, drop fast, and compute outside.
Poisoning note
A thread that panics while holding a guard poisons its mutex. Later locks return an error instead of silent access to possibly broken data. Into inner recovers the value when continued use is known to be safe.
use std::sync::Mutex;
fn main() {
let count = Mutex::new(0);
let mut guard = count.lock().unwrap_or_else(|e| e.into_inner());
*guard += 1;
}
Poisoning signals caution rather than forcing a shutdown. Unwrap keeps panicking on poison, which suits programs with no recovery plan. Choose into inner only after auditing what the panic may have left behind.
Next: Files
Article author: Arthur Isaev