1. Problem It Solves
A data race is invalid unsynchronized access, deadlock is circular waiting, and false sharing is cache-line contention between logically independent writes. It makes an important constraint visible instead of leaving readers to guess. This lesson keeps only the C++11 core that fits one focused day.
2. Prerequisites
The ideas from Day 49, plus basic variables, functions, and output already introduced.
3. Core Idea
Mental model: A data race is invalid unsynchronized access, deadlock is circular waiting, and false sharing is cache-line contention between logically independent writes. Identify the relevant value or state, who owns it, and whether the rule acts during compilation or execution.
4. Minimal Syntax
std::lock(a, b); alignas(64) std::atomic<int> counter;5. How It Works
The example creates a tiny fixed state with no keyboard input.
C++11 or the standard-library contract applies today's rule.
The program prints the important result so it can be checked against the source.
6. Common Mistakes
Adding atomics can remove a data race yet still leave false sharing, while inconsistent mutex order can deadlock otherwise correct state updates.
7. When to Use It
Use it when auditing concurrent correctness first and cache contention second.
Avoid it when it hides ownership, lifetime, type, ordering, or cost.
8. Simple Example
Two threads update padded atomic counters, and std::lock acquires two mutexes without imposing a dangerous manual order. The .cpp keeps the data fixed and avoids unrelated abstraction.
Complete sample code
Source file
cpp11/50_data_race_deadlock_false_sharing/main.cpp
#include <atomic>
#include <iostream>
#include <mutex>
#include <thread>
struct alignas(64) PaddedCounter {
PaddedCounter() : value(0) {}
std::atomic<int> value;
};
int main() {
PaddedCounter left;
PaddedCounter right; // padding reduces likely cache-line sharing
std::thread first([&] {
for (int i = 0; i < 1000; ++i) {
left.value.fetch_add(1, std::memory_order_relaxed);
}
});
std::thread second([&] {
for (int i = 0; i < 1000; ++i) {
right.value.fetch_add(1, std::memory_order_relaxed);
}
});
std::mutex a;
std::mutex b;
std::lock(a, b); // deadlock-aware acquisition
std::lock_guard<std::mutex> lock_a(a, std::adopt_lock);
std::lock_guard<std::mutex> lock_b(b, std::adopt_lock);
first.join();
second.join();
std::cout << left.value << ',' << right.value << '\n';
}
9. Key Takeaways
The feature is part of the C++11 scope used in this course.
Understand its lifetime, ownership, type, and ordering consequences.
Compile with warnings and prefer the smallest form that makes the rule obvious.
10. Self-Check Questions
Easy — How do data races, deadlocks, and false sharing differ? Which causes undefined behavior, which blocks thread progress, and which primarily harms performance?
Medium — Read the small example described above. What value or state should it print, and which rule produces that result?
Hard — Find and explain the subtle bug in this situation: Adding atomics can remove a data race yet still leave false sharing, while inconsistent mutex order can deadlock otherwise correct state updates. What is the smallest C++11-safe correction?