What is a Race Condition?
Race conditions explained with a shared-counter example, why they happen, and how mutexes and atomics fix them for interviews.
Expected Interview Answer
A race condition occurs when two or more threads or processes access shared data concurrently and the final outcome depends on the unpredictable timing of their execution, producing incorrect results when the interleaving goes wrong.
The classic example is two threads both reading a shared counter, incrementing it, and writing it back: if their read-modify-write sequences interleave, one increment can be silently lost because both threads based their write on the same stale value. Race conditions happen because operations that look atomic in source code, like "count++", actually compile to multiple non-atomic CPU instructions that can be interrupted between the read and the write. They are fixed by enforcing mutual exclusion around the shared data using locks, mutexes, semaphores, or atomic instructions so only one thread can execute the critical section at a time. Race conditions are notoriously hard to debug because they are timing-dependent and may not reproduce reliably under a debugger.
- Understanding races explains why locks and mutexes exist
- Recognizing non-atomic operations prevents subtle data corruption
- Knowing the pattern helps design safe concurrent APIs
- Awareness improves code review of multi-threaded changes
AI Mentor Explanation
Two batsmen both see the ball roll toward the boundary and both start running for an extra run without checking with each other first. If their timing overlaps badly, they can collide mid-pitch or both end up stranded at the same end, ruining the play. That unpredictable, timing-dependent bad outcome from uncoordinated shared action is exactly a race condition.
Step-by-Step Explanation
Step 1
Shared state
Two or more threads have access to the same variable or resource without protection.
Step 2
Non-atomic operation
The operation on that state (like read-modify-write) takes multiple steps that can be interleaved.
Step 3
Bad interleaving
The scheduler interleaves the threads in an order where one thread’s update is lost or seen inconsistently.
Step 4
Fix
Wrap the critical section in a lock, mutex, semaphore, or use an atomic instruction so the sequence executes as one unit.
What Interviewer Expects
- A concrete example like a lost counter increment
- Explanation that source-level operations are not atomic at the CPU level
- Naming synchronization primitives that fix races (mutex, semaphore, atomics)
- Acknowledgment that races are timing-dependent and hard to reproduce
Common Mistakes
- Confusing a race condition with a deadlock
- Assuming simple statements like count++ are atomic
- Not proposing a concrete fix like a mutex
- Thinking race conditions only happen with threads, ignoring multi-process shared memory
Best Answer (HR Friendly)
“A race condition happens when two parts of a program try to use the same piece of shared data at the same time, and the result depends on which one happens to go first. It is fixed by making sure only one of them can touch that shared data at a time, using a lock.”
Code Example
#include <pthread.h>
#include <stdio.h>
long counter = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *racy_increment(void *arg) {
for (int i = 0; i < 100000; i++) {
counter++; /* NOT atomic: read, add, write */
}
return NULL;
}
void *safe_increment(void *arg) {
for (int i = 0; i < 100000; i++) {
pthread_mutex_lock(&lock);
counter++; /* protected critical section */
pthread_mutex_unlock(&lock);
}
return NULL;
}
int main(void) {
pthread_t t1, t2;
pthread_create(&t1, NULL, safe_increment, NULL);
pthread_create(&t2, NULL, safe_increment, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("counter = %ld (expected 200000)\n", counter);
return 0;
}Follow-up Questions
- What is the critical section problem and how does it relate to race conditions?
- What is the difference between a mutex and a semaphore?
- How do atomic instructions like compare-and-swap avoid locking overhead?
- What tools can detect race conditions in production code?
MCQ Practice
1. A race condition is most directly caused by?
Race conditions arise specifically when concurrent access to shared data is not properly synchronized.
2. Which of these best prevents a race condition on a shared counter?
A mutex enforces mutual exclusion so only one thread updates the counter at a time.
3. Why is "count++" not safe across threads without protection?
The increment expands to a read, an add, and a write, and another thread can interleave between those steps.
Flash Cards
What is a race condition? — A timing-dependent bug from unsynchronized concurrent access to shared data.
Classic race condition example? — Two threads incrementing a shared counter and losing an update.
Why is count++ unsafe across threads? — It is really a read, add, and write — three steps that can interleave.
How do you fix a race condition? — Protect the shared data with a lock, mutex, semaphore, or atomic operation.