Post

Parallel - Race Condition

Parallel - Race Condition

🧨 Race Condition — When Threads Corrupt Shared Data

A race condition happens when multiple threads access and modify shared data at the same time,
and the program’s behavior depends on timing or execution order.

In short:

Threads “race” to update shared memory — causing unpredictable results.


1️⃣ Why Race Conditions Happen

Race conditions occur when:

✔ Shared data exists
✔ Multiple threads modify it
✔ No synchronization is used

Example shared resources:

  • Global variables
  • Heap memory
  • Files
  • Shared containers (vectors, maps)

2️⃣ Real‑World Analogy

Two people editing the same Google Doc at the same time without autosync → data becomes inconsistent.


3️⃣ Broken Example — Race Condition in C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <thread>

int counter = 0;

void increment() {
    for (int i = 0; i < 100000; i++)
    {
        counter++; // ❌ Not thread‑safe
    }
}

int main() 
{
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Counter = " << counter << "\n"; 
}

Expected output:

1
Counter = 200000

Actual output (varies):

1
2
Counter = 143982
Counter = 178221

❌ Because both threads modify counter simultaneously.


4️⃣ Why It Breaks — CPU Instruction Level

counter++ is not atomic:

1
2
3
Load counter
Add 1
Store counter

If threads interleave:

1
2
3
4
Thread A loads counter = 10
Thread B loads counter = 10
Thread A stores 11
Thread B stores 11   ❌ lost update

5️⃣ Fix Example — Using Mutex

1
2
3
4
5
6
7
8
9
10
11
12
#include <mutex>

int counter = 0;
std::mutex m;

void increment() {
    for (int i = 0; i < 100000; i++) 
    {
        std::lock_guard<std::mutex> lock(m);
        counter++; // ✅ protected
    }
}

✔ Prevents simultaneous access
✔ Ensures correct result


6️⃣ Faster Fix — Atomic Variables

1
2
3
4
5
6
7
8
9
10
11
#include <atomic>

std::atomic<int> counter(0);

void increment() 
{
    for (int i = 0; i < 100000; i++) 
    {
        counter++; // ✅ atomic
    }
}

✔ Lock‑free
✔ Faster than mutex in many cases


7️⃣ Common Race Condition Scenarios

ScenarioRisk
Updating shared countersWrong values
Writing logsMixed/corrupted logs
Modifying STL containersCrashes
Banking/account balanceFinancial errors
Game state updatesBroken logic

8️⃣ How Developers Prevent Race Conditions

✔ Mutex / lock_guard
✔ Atomic variables
✔ Read‑write locks
✔ Thread‑safe data structures
✔ Minimize shared state
✔ Immutable data patterns

This post is licensed under CC BY 4.0 by the author.