← SEARCHABLE ARCHIVE
Operating System · 2026-07-15

Thread synchronization and race conditions

Why do shared data results change every time I run it?

INTERACTIVE READING MODE →

The articles written on this blog were created with the goal of teaching. Also, the information in the article may be incorrect, so please be careful.

Threads in the same process share heap and global data. Thanks to this, data can be exchanged quickly, but if the same value is read and written simultaneously, the results may vary each time it is executed.

This problem is called Race Condition.

count++ is not a one-time operation

int count = 0;

void AddCount()
{
    ++count;
}

It may be one line of code, but on the CPU it could be multiple steps: reading a value, adding 1, and storing it again.

Thread A reads count 0
Thread B reads count 0
Thread A stores 1
Thread B stores 1

We increment it twice, but the final result is 1, not 2. Since the execution order of threads varies depending on the operating system situation, this may be a bug that appears only occasionally during testing.

Mutex allows only one thread to enter at a time

std::mutex countMutex;
int count = 0;

void AddCount()
{
    std::lock_guard<std::mutex> lock(countMutex);
    ++count;
}

While the thread that locked the mutex executes the critical section, other threads wait. lock_guardIf you use , the lock is automatically released even when the function ends or an exception occurs.

Atomic can be used for simple values

Atomic operations can be used for small operations, such as increasing an integer.

std::atomic<int> count = 0;

void AddCount()
{
    ++count;
}

However, if the relationships between multiple variables need to be safely maintained at the same time, it cannot be solved with just one atomic. For example, if gold deduction and item addition must succeed together, both must be protected by a single critical area.

You should also be careful about deadlocks

If two threads wait for a lock held by the other, no one can proceed. This is called deadlock.

Thread A: Holds Lock 1 and waits for Lock 2
Thread B: Holds Lock 2 and waits for Lock 1

You need to get into the habit of always acquiring locks in the same order and not doing long-running tasks while holding the lock.

The point of multithreading is not creating many threads. Reduce shared data, clearly define the scope of what must be shared, and use a synchronization tool that fits that scope.