asynchronousrustmutexatomicrwlock

Can I use atomics in an asynchronous context?


Is there a way to use atomic types in an asynchronous context instead of an asynchronous Mutex or RwLock? Can standard atomics be used as is in an asynchronous context?

Or, for example, is there an asynchronous equivalent of std::sync::atomic::AtomicUsize with load / store methods, which could replace something like tokio::sync::RwLock<usize> with read().await / write().await methods?


Solution

  • Yes, using Atomics in an async context is no problem.

    Most of them are lock-free (=can't even block).

    Even if you would block, it is still recommended to use normal, blocking synchronization primitives over async ones, unless you hold that lock during an await.

    For more info I quote the respective chapter of the tokio tutorial:

    On using std::sync::Mutex

    Note, std::sync::Mutex and not tokio::sync::Mutex is used to guard the HashMap. A common error is to unconditionally use tokio::sync::Mutex from within async code. An async mutex is a mutex that is locked across calls to .await.

    A synchronous mutex will block the current thread when waiting to acquire the lock. This, in turn, will block other tasks from processing. However, switching to tokio::sync::Mutex usually does not help as the asynchronous mutex uses a synchronous mutex internally.

    As a rule of thumb, using a synchronous mutex from within asynchronous code is fine as long as contention remains low and the lock is not held across calls to .await. Additionally, consider using parking_lot::Mutex as a faster alternative to std::sync::Mutex.

    Note that this is of course only true if all the threads accessing said lock/atomic are also async worker threads. If you have external threads that could block the mutex, you have to consider waiting for that mutex a blocking operation and treat it as such.