Overview:
Atomic variables are used in multithreaded programming to achieve synchronization without using mutexes, relying on hardware-supported atomic operations, which are generally more efficient than locks.
Common Methods
- load(): Retrieves the value from the atomic variable.
- store(val): Stores the value val into the atomic variable.
- oldVal exchange(val): Stores the value val into the atomic variable and returns the previous value of the atomic variable.
- Atomic variables do not support copy construction and assignment operations.
Memory Order
In the common functions of atomic variables, the last parameter is used to set the memory order of the atomic operation, with the default value being std::memory_order_seq_cst.
- std::memory_order_relaxed: Guarantees only the atomicity of the atomic operation, without providing memory ordering constraints.
- std::memory_order_consume: Load operations that depend on this atomic operation in the current thread cannot be reordered before this operation.
- std::memory_order_acquire: Subsequent load operations in the current thread cannot be reordered before this atomic operation.
- std::memory_order_release: Previous store operations in the current thread cannot be reordered after this atomic operation.
- std::memory_order_acq_rel: Has both acquire and release semantics.
- std::memory_order_seq_cst: The strictest memory order, ensuring that all threads see the same order of operations (default).
In practical programming, to simplify memory and ensure logical correctness, it is sufficient to use the default memory order, reducing the logical complexity of the code.
Available values and meanings of memory order in different operations.

Specific Methods
Special function support is provided for integer types (int, unsigned short, etc.) and pointers. Note that floating-point and ordinary objects are not supported.
-
oldVal fetch_add(addVal): Adds addVal to the atomic variable and returns the original atomic variable value (oldVal).
-
fetch_sub(subVal): Subtracts subVal from the atomic variable and returns the original atomic variable value (oldVal).
-
fetch_and(): Performs a bitwise AND operation.
-
fetch_or(): Performs a bitwise OR operation.
-
fetch_xor(): Performs a bitwise XOR operation.
-
Operator support: ++, –, +=, -=, |, |=, &, &=, etc., similar to the functionality of fetch_xxx() functions.
Simple Use of Atomic Operations
Atomic variables are commonly used for atomic counting, lock-free queues, spin locks, etc. The practical application of atomic calculations is very widespread, avoiding thread synchronization.
Basic Operations of Atomic Variables
#pragma once
#include <atomic>
#include "iostream"
inline void TestAtomicBaseUse() {
std::atomic<int> sAtInt(1);
std::atomic<int> sAtInt2 = 2;
// using atomic_int = atomic<int>; provides a simplified type
std::atomic_int sAtInt3 = 3;
// using store() function to modify the value in the atomic variable
sAtInt.store(10);
// fetch_add(addVal) increases the atomic variable value and returns the original value
std::cout << " fetch_add = " << sAtInt.fetch_add(2) << std::endl; // 10
// fetch_sub(subVal) decreases the atomic variable value and returns the original value
std::cout << " fetch_sub = " << sAtInt.fetch_add(-2) << std::endl; // 12
// Basic data types support ++ -- += -= etc.
sAtInt++;
std::cout << " ++ = " << sAtInt.load() << std::endl; // 11
sAtInt -= 1;
std::cout << " -= " << sAtInt.load() << std::endl; // 10
// exchange, value swap, returns the original value
std::cout << " fetch_sub = " << sAtInt.exchange(20) << std::endl; // 10
// using load() function to get the value in the atomic variable
std::cout << " load = " << sAtInt.load() << std::endl; // 20
}
Example of using atomic variables in multithreading
#include <atomic>
#include <thread>
#include <chrono>
#include <iostream>
inline void testAtMulThread() {
// Thread 1 increments the atomic count every 100ms;
// Thread 2 reads the count every 200ms and adds 10;
// Thread 3 reads the value every 100ms, exits when the value > 100;
// Variables captured by value need to support copy operations.
std::thread t1([=] {
while (true) {
if (tAtCount.load() > 100)
return;
tAtCount.fetch_add(1);
std::cout << " val 1 = " << tAtCount.load() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
});
std::thread t2([=] {
while (true) {
if (tAtCount.load() > 100)
return;
tAtCount.fetch_add(10);
std::cout << " val 2 = " << tAtCount.load() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
});
std::thread t3([=] {
while (true) {
if (tAtCount.load() > 100)
return;
std::cout << " val 3 = " << tAtCount.load() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
});
t1.detach();
t2.detach();
t3.detach();
}
This is purely for personal learning and understanding. Feel free to add me on WeChat for discussion and improvement. My WeChat ID is as follows:
