What is an "async" mutex as opposed to a "normal" mutex? I believe this is the difference between tokio's Mutex
and the normal std lib Mutex
. But I don't get, conceptually, how a mutex can be "async". Isn't the whole point that only one thing can use it at a time?
Here's a simple comparison of their usage:
let mtx = std::sync::Mutex::new(0);
let _guard = mtx.lock().unwrap();
let mtx = tokio::sync::Mutex::new(0);
let _guard = mtx.lock().await;
Both ensure mutual exclusivity. The only difference between an asynchronous mutex and a synchronous mutex is dictated by their behavior when trying to acquire a lock. If a synchronous mutex tries to acquire the lock while it is already locked, it will block execution on the thread. If an asynchronous mutex tries to acquire the lock while it is already locked, it will yield execution to the executor.
If your code is synchronous, there's no reason to use an asynchronous mutex. As shown above, locking an asynchronous mutex is Future
-based and is designed to be using in async
/await
contexts.
If your code is asynchronous, you may still want to use a synchronous mutex since there is less overhead. However, you should be mindful that blocking in an async
/await
context is to be avoided at all costs. Therefore, you should only use a synchronous mutex if acquiring the lock is not expected to block. Some cases to keep in mind:
- If you need to hold the lock over an
.await
call, use an asynchronous mutex. The compiler will usually reject this anyway when using thread-safe futures since most synchronous mutex locks can't be sent to another thread. - If your lock is contentious (i.e. if you expect the mutex to already be locked when you want it), you should use an asynchronous mutex. This can happen when synchronizing multiple tasks into a pool or bounded queue.
- If you have complicated and/or computationally-heavy updates, those should probably be moved to a blocking pool anyway where you'd use a synchronous mutex.
The above cases are all three sides of the same coin: if you expect to block, use an asynchronous mutex. If you don't know whether your mutex usage will block or not, err on the side of caution and use an asynchronous mutex. Using an asynchronous mutex where a synchronous one would suffice only leaves a small amount of performance on the table, but using a synchronous mutex where you should've used an asynchronous one could be catastrophic.
Most situations I run into with mutexes are when synchronizing simple data structures, where the update methods are well-encapsulated to acquire the lock, update the data, and release the lock. Did you know a simple println!
requires locking a mutex? Those uses of mutexes can be synchronous and used even in an asynchronous context. Even if the lock does block, it often is no more impactful than a process context switch which happens all the time anyway.
Note: Tokio's Mutex
does have a .blocking_lock()
method which is helpful if both locking behaviors are needed. So the mutex can be both synchronous and asynchronous!
See also:
- Why do I get a deadlock when using Tokio with a std::sync::Mutex?
std::sync::Mutex
vsfutures:lock:Mutex
vsfutures_lock::Mutex
for async on the Rust forum- Which kind of mutex should you use? in the Tokio documentation
- On using
std::sync::Mutex
in the Tokio tutorial on shared state
.await
, but many async frameworks require thread-safe tasks by default and the standard Mutex
's lock guard is not thread-safe so you'd often run into issues there. You could use the Mutex
from the parking-lot crate if you wanted to since it's guard is thread-safe. –
Ammamaria .await
s is not exactly due to the thread-safety issue but rather because during a .await
the task is suspended (and therefore the lock is held) for an unpredictable amount of time (often due to I/O). Thus when acquiring the lock elsewhere in an asynchronous context, you know it may be held for potentially a long time, so you want to use an asynchronous mutex to avoid blocking the executor. You can even get yourself into deadlocks if you don't (depending on the executor's work-stealing behavior). –
Ammamaria © 2022 - 2025 — McMap. All rights reserved.