Holding a standard Mutex guard across .await blocks the entire thread!
rustlet lock = std_mutex.lock().unwrap(); do_something().await; // BAD! Thread blocked while holding lock
Why? The std Mutex guard is !Send, and blocking on it wastes the thread.
tokio::sync::Mutex
Async-aware mutex that yields instead of blocking:
rustuse tokio::sync::Mutex; let mutex = Mutex::new(0); async fn increment(m: &Mutex<i32>) { let mut lock = m.lock().await; // Yields, doesn't block thread *lock += 1; // Lock can be held across await points some_async_work().await; *lock += 1; }
When to Use Which?
| Scenario | Use |
|---|---|
| Short, sync operations | std::sync::Mutex |
Lock held across .await | tokio::sync::Mutex |
| High contention, async | tokio::sync::Mutex |
| Performance-critical, no await | std::sync::Mutex |
Important: std::sync::Mutex is often fine if you drop the lock before .await:
rustlet value = { let lock = std_mutex.lock().unwrap(); lock.clone() }; // Lock dropped here do_something(value).await; // OK!
RwLock for Read-Heavy Workloads
rustuse tokio::sync::RwLock; let lock = RwLock::new(vec![1, 2, 3]); // Multiple readers OK let r1 = lock.read().await; let r2 = lock.read().await; // Writer needs exclusive access let mut w = lock.write().await; w.push(4);
See Shared State.
Code Examples
rust
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() {
let data = Arc::new(Mutex::new(vec![]));
let mut handles = vec![];
for i in 0..10 {
let data = Arc::clone(&data);
handles.push(tokio::spawn(async move {
let mut lock = data.lock().await;
lock.push(i);
}));
}
for handle in handles {
handle.await.unwrap();
}
println!("{:?}", data.lock().await);
}