![]() |
VOOZH | about |
In C++, data race is a commonly occurring problem in concurrent programming. It occurs when two or more threads concurrently access the same memory location, and at least one of the accesses is a write. Data races lead to undefined behaviour, which means the program can exhibit unpredictable behavior, crash, or produce incorrect results. Preventing data races is crucial for writing safe and reliable concurrent programs.
In this article we will discuss what are data races, what are its causes and how we can prevent them from happening in our program.
There are many possible causes of the data races on C++. It can occur due to the concurrent execution of threads without proper synchronization. In a multi-threaded environment, if threads do not coordinate their access to shared data, it can result in:
The following program demonstrate the data race condition in a concurrent program:
Output 1
Counter value: 146377Output 2
Counter value: 200000As seen from the above program, we may get different value in every execution. This is due to the data race condition.
We can prevent the data races from happening by synchronizing the thread using various synchronizing primitives available in C++. Some of the are as follows:
A std::mutex can be used to ensure mutual exclusion, meaning that only one thread can access the critical section (shared data) of code at a time. Locking a mutex before accessing shared data ensures that only one thread can access the data at a time. We can unlock it when we are done working on it.
Syntax
std::mutex name // creating mutex
// In the callable
lock_guard<mutex> lock_name(name) // locking mutex
The mutex locked using lock_guard does not need to be explicitly unlocked at the end. They automatically gets unlocked when lock goes out of scope.
Example:
Output
Counter: 200000To know more about mutex in C++, refer to the article - Mutex in C++
In C++, std::atomic provides atomic operations on fundamental data types, which means these operations are performed as a single, indivisible step. Atomic operations prevent data races without the need for explicit locks.
Syntax
std::atomic <type> var_name;Example
Output
Counter: 200000To know more about C++ Atomics, refer to the article - C++ 11 - <atomic> Header
Condition variables are used to block a thread until a particular condition is met. They are used in conjunction with a mutex to wait for or signal changes in shared data.
Syntax
condition_variable name;
Example:
Output
Start Waiting
Notification Sent from the other thread
Resuming the work after notification
To know more about condition variable, refer to the article - Condition Variables in C++ Multithreading