C++ Atomic | A complete guide to Memory Order
이 글의 핵심
A comprehensive guide to C++ atomic operations and memory ordering: relaxed, acquire, release, seq_cst, and practical synchronization patterns.
atomic basic```cpp
#include
atomic
void increment() { for (int i = 0; i < 1000; i++) { counter++; // Atomic operations } }
int main() { thread t1(increment); thread t2(increment);
t1.join();
t2.join();
cout << counter << endl; // 2000 (always correct) }
## atomic vs mutex
```cpp
// use mutex
mutex mtx;
int counter = 0;
void incrementMutex() {
for (int i = 0; i < 1000; i++) {
lock_guard<mutex> lock(mtx);
counter++;
}
}
// use atomic (faster)
atomic<int> atomicCounter(0);
void incrementAtomic() {
for (int i = 0; i < 1000; i++) {
atomicCounter++;
}
}
```## Memory Order
### memory_order_relaxed```cpp
atomic<int> x(0);
atomic<int> y(0);
// thread 1
void thread1() {
x.store(1, memory_order_relaxed);
y.store(1, memory_order_relaxed);
}
// thread 2
void thread2() {
while (y.load(memory_order_relaxed) == 0);
// There is no guarantee that x is 1!
cout << x.load(memory_order_relaxed) << endl;
}
memory_order_acquire/release
atomic<int> data(0);
atomic<bool> ready(false);
// producer
void producer() {
data.store(42, memory_order_relaxed);
ready.store(true, memory_order_release); // release
}
// consumer
void consumer() {
while (!ready.load(memory_order_acquire)); // acquire
cout << data.load(memory_order_relaxed) << endl; // 42 guaranteed
}
```### memory_order_seq_cst (default)```cpp
atomic<int> x(0);
atomic<int> y(0);
// Ensure sequential consistency
x.store(1, memory_order_seq_cst);
y.store(1, memory_order_seq_cst);
// All threads run in the same order
```## Practical example
### Example 1: Spinlock```cpp
class SpinLock {
private:
atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
while (flag.test_and_set(memory_order_acquire)) {
// spin (wait)
}
}
void unlock() {
flag.clear(memory_order_release);
}
};
int main() {
SpinLock spinlock;
int counter = 0;
auto increment = [&]() {
for (int i = 0; i < 1000; i++) {
spinlock.lock();
counter++;
spinlock.unlock();
}
};
thread t1(increment);
thread t2(increment);
t1.join();
t2.join();
cout << counter << endl; // 2000
}
```### Example 2: Lock-Free Stack```cpp
template<typename T>
class LockFreeStack {
private:
struct Node {
T data;
Node* next;
Node(T d) : data(d), next(nullptr) {}
};
atomic<Node*> head;
public:
LockFreeStack() : head(nullptr) {}
void push(T value) {
Node* newNode = new Node(value);
newNode->next = head.load(memory_order_relaxed);
while (!head.compare_exchange_weak(
newNode->next, newNode,
memory_order_release,
memory_order_relaxed
));
}
bool pop(T& result) {
Node* oldHead = head.load(memory_order_relaxed);
while (oldHead && !head.compare_exchange_weak(
oldHead, oldHead->next,
memory_order_acquire,
memory_order_relaxed
));
if (oldHead) {
result = oldHead->data;
delete oldHead;
return true;
}
return false;
}
};
int main() {
LockFreeStack<int> stack;
thread t1([&]() {
for (int i = 0; i < 100; i++) {
stack.push(i);
}
});
thread t2([&]() {
int value;
for (int i = 0; i < 50; i++) {
if (stack.pop(value)) {
cout << value << " ";
}
}
});
t1.join();
t2.join();
}
```### Example 3: Double check locking```cpp
class Singleton {
private:
static atomic<Singleton*> instance;
static mutex mtx;
Singleton() {}
public:
static Singleton* getInstance() {
Singleton* tmp = instance.load(memory_order_acquire);
if (tmp == nullptr) {
lock_guard<mutex> lock(mtx);
tmp = instance.load(memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton();
instance.store(tmp, memory_order_release);
}
}
return tmp;
}
};
atomic<Singleton*> Singleton::instance(nullptr);
mutex Singleton::mtx;
compare_exchange
atomic<int> value(0);
int expected = 0;
int desired = 10;
// Weak version (possible spurious failure)
if (value.compare_exchange_weak(expected, desired)) {
cout << "success" << endl;
} else {
cout << "Failed, current value: " << expected << endl;
}
// Strong version (no spurious failure)
if (value.compare_exchange_strong(expected, desired)) {
cout << "success" << endl;
}
```## Organize memory order
| order | Description | Usage Scenarios |
|------|------|-------------|
| relaxed | No order guarantee | counter |
| acquire | Prevent future read/write relocations | Acquire Rock |
| release | Prevent previous read/write relocation | unlock |
| acq_rel | acquire + release | RMW operation |
| seq_cst | sequential consistency (default) | When in doubt |
## Frequently occurring problems
### Issue 1: Incorrect memory order```cpp
// ❌ Data Race
atomic<bool> ready(false);
int data = 0;
void producer() {
data = 42;
ready.store(true, memory_order_relaxed); // Wrong!
}
void consumer() {
while (!ready.load(memory_order_relaxed));
cout << data << endl; // May not be 42!
}
// ✅ Correct order
void producer() {
data = 42;
ready.store(true, memory_order_release);
}
void consumer() {
while (!ready.load(memory_order_acquire));
cout << data << endl; // 42 guaranteed
}
```### Issue 2: ABA Issues```cpp
// ❌ ABA Problems
atomic<Node*> head;
void pop() {
Node* oldHead = head.load();
// Here, other threads can pop and push.
head.compare_exchange_strong(oldHead, oldHead->next);
// oldHead may be a different node!
}
// ✅ Use tag pointers
struct TaggedPointer {
Node* ptr;
uintptr_t tag;
};
atomic<TaggedPointer> head;
```### Problem 3: Incorrect use of atomic```cpp
// ❌ Non-atomic type
struct Big {
int data[100];
};
atomic<Big> a; // Compilation error or lock based
// ✅ Small types only
atomic<int> a;
atomic<bool> b;
atomic<void*> c;
```## Performance considerations```cpp
// relaxed (fastest)
counter.fetch_add(1, memory_order_relaxed);
// acquire/release (medium)
flag.store(true, memory_order_release);
// seq_cst (slowest, default)
counter.fetch_add(1, memory_order_seq_cst);
```## FAQ
### Q1: When do you use atomic?
**A**:
- Simple counter
- flag
- Lock-free data structure
### Q2: atomic vs mutex?
**A**:
- **atomic**: simple operation, fast
- **mutex**: complex operation, multiple variables
### Q3: How do I select memory_order?
**A**:
- seq_cst (default) if in doubt
- acquire/release if performance is important
- Simple counter is relaxed
### Q4: Is lock-free always faster?
**A**: No. If there is a lot of contention, a mutex may be faster.
### Q5: What is atomic debugging?
**A**:
- Use ThreadSanitizer
- Added logging
- Test starting from simple cases
### Q6: What are atomic learning resources?
**A**:
- "C++ Concurrency in Action" (Anthony Williams)
- cppreference.com
- Preshing on Programming Blog
---
## Good article to read together (internal link)
Here's another article related to this topic.
- [C++ Memory Order | “Memory Order” Guide](/blog/cpp-memory-order/)
- [C++ Atomic Operations | “Atomic Operations” Guide](/blog/cpp-atomic-operations/)
- [C++ Lock-Free Programming | "Lock Free Programming" Guide](/blog/cpp-lock-free-programming/)
## Practical tips
These are tips that can be applied right away in practice.
### Debugging tips
- If you run into a problem, check the compiler warnings first.
- Reproduce the problem with a simple test case
### Performance Tips
- Don't optimize without profiling
- Set measurable indicators first
### Code review tips
- Check in advance for areas that are frequently pointed out in code reviews.
- Follow your team's coding conventions
---
## Practical checklist
This is what you need to check when applying this concept in practice.
### Before writing code
- [ ] Is this technique the best way to solve the current problem?
- [ ] Can team members understand and maintain this code?
- [ ] Does it meet the performance requirements?
### Writing code
- [ ] Have you resolved all compiler warnings?
- [ ] Have you considered edge cases?
- [ ] Is error handling appropriate?
### When reviewing code
- [ ] Is the intent of the code clear?
- [ ] Are there enough test cases?
- [ ] Is it documented?
Use this checklist to reduce mistakes and improve code quality.
---
## Keywords covered in this article (related search terms)
This article will be helpful if you search for C++, atomic, memory-order, concurrency, lock-free, etc.
---
## Related articles
- [C++ Atomic Operations | ](/blog/cpp-atomic-operations/)
- [C++ Lock-Free Programming | ](/blog/cpp-lock-free-programming/)
- [C++ Memory Order | ](/blog/cpp-memory-order/)
- [C++ Lock-Free Programming Practice | CAS, ABA, memory order, high-performance queue [#34-3]](/blog/cpp-series-34-3-lock-free/)
- [C++ Lock-Free Programming Practice | CAS, ABA, memory order, high-performance queue [#51-5]](/blog/cpp-series-51-5-lock-free-programming/)