C++ Mutex & Locks
In this page:
Data Races and Mutex
A data race occurs when multiple threads try to write to the same shared memory location at the same time, leading to corrupted data. You can protect shared data using a 'std::mutex' to ensure only one thread can access it at a time.
Example: Data Races and Mutex
#include <iostream>
#include <mutex>
std::mutex m;
int counter = 0;
void increment() {
m.lock();
counter++; // protected from concurrent writes
m.unlock();
}
int main() {
increment();
std::cout << counter << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Lock Guard (std::lock_guard)
Using manual lock() and unlock() can be dangerous because if your code throws an exception or returns early, the mutex stays locked forever. Use 'std::lock_guard' to lock a mutex. It automatically unlocks the mutex when it goes out of scope.
Example: Lock Guard (std::lock_guard)
#include <iostream>
#include <mutex>
std::mutex m;
int counter = 0;
void increment() {
std::lock_guard<std::mutex> lock(m); // auto-unlocks, even on early return/exception
counter++;
}
int main() {
increment();
std::cout << counter << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Unique Lock (std::unique_lock)
The 'std::unique_lock' class is a more flexible version of lock_guard. It allows you to manually lock and unlock the mutex at any point, deferred lock acquisitions, and manage time-delayed locks.
Example: Unique Lock (std::unique_lock)
#include <iostream>
#include <mutex>
std::mutex m;
int main() {
std::unique_lock<std::mutex> lock(m); // can unlock/relock manually
lock.unlock();
lock.lock();
std::cout << "Manually managed lock" << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Deadlocks
A deadlock occurs when two threads are blocked forever, each waiting for a mutex held by the other. To avoid deadlocks, use C++17's 'std::scoped_lock' to lock multiple mutexes safely and simultaneously.
Example: Deadlocks
#include <iostream>
#include <mutex>
std::mutex m1, m2;
int main() {
std::lock(m1, m2); // locks both without risking a deadlock, unlike locking separately
std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
std::cout << "Both locked safely" << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Best Practices for Shared Memory
Keep your critical sections (the parts of your code that are locked) as small as possible. This ensures that threads spend less time waiting for locks, which improves program throughput.
Example: Best Practices for Shared Memory
#include <iostream>
#include <mutex>
std::mutex m;
int counter = 0;
void increment() {
{
std::lock_guard<std::mutex> lock(m); // critical section kept as small as possible
counter++;
}
std::cout << "Non-critical work outside the lock" << std::endl;
}
int main() {
increment();
std::cout << counter << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Chapter Quiz — Complete all 17 topics to unlock
0/17 topics done
Complete these topics first:
- C++ vs C Differences
- C++ Interview Questions
- C++ Debugging Techniques
- C++ Input Validation
- C++ Namespaces
- C++ Header Files
- C++ Multi-file Programming
- C++ static_cast
- C++ dynamic_cast
- C++ const_cast
- C++ reinterpret_cast
- C++ Threads (std::thread)
- C++ Mutex & Locks
- C++ async & future
- C++ Mini Project — Calculator
- C++ Mini Project — Student Management
- C++ Interview Questions Advanced