Introduction
The Mutex
class in Rust provides mutual exclusion, allowing only one thread to access the data at a time. It is a fundamental synchronization primitive useful for protecting shared data from being simultaneously accessed by multiple threads.
Using Mutex
The Mutex
class can be used to create a thread-safe wrapper around a piece of data. Here is a basic example:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
// Output: Result: 10
Key Methods
Below are some of the key methods exposed by the Mutex
class:
new
Creates a new Mutex
instance wrapping the given data.
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}
println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }
lock
Acquires a lock on the mutex, blocking the current thread until it is able to do so. Returns a MutexGuard
which allows access to the underlying data.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(0));
let data_clone = Arc::clone(&data);
let handle = thread::spawn(move || {
let mut num = data_clone.lock().unwrap();
*num = 42;
});
handle.join().unwrap();
println!("Data: {}", *data.lock().unwrap());
}
// Output: Data: 42
try_lock
Attempts to acquire the lock on the mutex without blocking. Returns a Result
which is Ok(MutexGuard)
if the lock was acquired, or Err(TryLockError)
if it was not.
use std::sync::{Mutex, TryLockError};
fn main() {
let m = Mutex::new(5);
match m.try_lock() {
Ok(mut num) => *num = 6,
Err(TryLockError::WouldBlock) => println!("Could not acquire lock"),
Err(TryLockError::Poisoned(err)) => println!("Lock is poisoned: {}", err),
}
println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }
into_inner
Consumes the mutex, returning the underlying data. This will block if the mutex is currently locked.
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
let data = m.into_inner().unwrap();
println!("Data: {}", data);
}
// Output: Data: 5
get_mut
Returns a mutable reference to the underlying data without acquiring the lock. This method is safe to call only when there are no active references to the mutex.
use std::sync::Mutex;
fn main() {
let mut m = Mutex::new(5);
*m.get_mut().unwrap() = 6;
println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }
Example Usage
Example 1: Basic Usage
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
// Output: Result: 10
Example 2: Lock and Modify Data
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}
println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }
Example 3: Try Lock
use std::sync::{Mutex, TryLockError};
fn main() {
let m = Mutex::new(5);
match m.try_lock() {
Ok(mut num) => *num = 6,
Err(TryLockError::WouldBlock) => println!("Could not acquire lock"),
Err(TryLockError::Poisoned(err)) => println!("Lock is poisoned: {}", err),
}
println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }
Example 4: Into Inner
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
let data = m.into_inner().unwrap();
println!("Data: {}", data);
}
// Output: Data: 5
Example 5: Get Mut
use std::sync::Mutex;
fn main() {
let mut m = Mutex::new(5);
*m.get_mut().unwrap() = 6;
println!("m = {:?}", m);
}
// Output: m = Mutex { data: 6 }
Considerations
Mutex
can cause deadlocks if not used carefully. Always ensure that locks are released appropriately.- A
Mutex
can become poisoned if a thread panics while holding the lock. Accessing a poisonedMutex
will result in aPoisonError
. - Use
Arc
(Atomic Reference Counting) to share aMutex
across multiple threads.
See Also
- RwLock - A reader-writer lock for allowing concurrent reads or exclusive writes.
- Condvar - A condition variable for thread synchronization.
- AtomicUsize - A thread-safe atomic integer type.
- Arc - An atomic reference-counted smart pointer for shared ownership across threads.