Acquire and Release Fences
The most obvious difference between acquire and release fences and atomics with acquire-release semantics is that fences need no atomics. There is also a more subtle difference: the acquire and release fences are more heavyweight.
Atomic Operations vs Fences
For the sake of simplicity, I will now refer to acquire operations when I use fences or atomic operations with acquire semantic. The same will hold for release operations.
The main idea of an acquire and a release operation is that it establishes synchronization and ordering constraints between threads. These synchronization and ordering constraints also hold for atomic operations with relaxed semantic or non-atomic operations. Note that acquire and release operations come in pairs. In addition, operations on atomic variables with acquire-release semantic must act on the same atomic variable. Having said that, I will now look at these operations in isolation. Let’s start with the acquire operation.
A load (read) operation on an atomic variable with the memory model set to
std::memory_order_acquire is an acquire operation.