0
0
CppHow-ToBeginner · 3 min read

How to Use Mutex in C++ for Thread Safety

In C++, use std::mutex to protect shared data from being accessed by multiple threads at the same time. Lock the mutex with lock() before accessing shared resources and unlock it with unlock() after. Alternatively, use std::lock_guard to automatically manage locking and unlocking.
📐

Syntax

The basic syntax to use a mutex involves creating a std::mutex object and then locking it before accessing shared data. You unlock it after the access is done to allow other threads to proceed.

  • std::mutex mtx; - declares a mutex object.
  • mtx.lock(); - locks the mutex, blocking if already locked.
  • mtx.unlock(); - unlocks the mutex.
  • std::lock_guard lock(mtx); - RAII style lock that locks on creation and unlocks automatically when it goes out of scope.
cpp
std::mutex mtx;

// Lock and unlock manually
mtx.lock();
// critical section
mtx.unlock();

// Using lock_guard for automatic locking
{
    std::lock_guard<std::mutex> lock(mtx);
    // critical section
} // mutex unlocks here automatically
💻

Example

This example shows two threads incrementing a shared counter safely using std::mutex. The mutex ensures only one thread changes the counter at a time, preventing errors.

cpp
#include <iostream>
#include <thread>
#include <mutex>

int counter = 0;
std::mutex mtx;

void increment(int times) {
    for (int i = 0; i < times; ++i) {
        std::lock_guard<std::mutex> lock(mtx); // lock mutex
        ++counter; // safely increment shared counter
    }
}

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

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

    std::cout << "Final counter value: " << counter << std::endl;
    return 0;
}
Output
Final counter value: 20000
⚠️

Common Pitfalls

Common mistakes when using mutexes include:

  • Forgetting to unlock the mutex, causing deadlocks.
  • Locking the mutex multiple times in the same thread without using std::recursive_mutex.
  • Not locking the mutex at all, leading to race conditions.
  • Holding the lock for too long, reducing concurrency.

Using std::lock_guard helps avoid forgetting to unlock.

cpp
std::mutex mtx;

// Wrong: forgetting to unlock
void wrong() {
    mtx.lock();
    // critical section
    // forgot mtx.unlock(); -> deadlock risk
}

// Right: use lock_guard
void right() {
    std::lock_guard<std::mutex> lock(mtx);
    // critical section
} // unlocks automatically
📊

Quick Reference

OperationDescriptionExample
Declare mutexCreate a mutex objectstd::mutex mtx;
Lock mutexBlock other threads until unlockedmtx.lock();
Unlock mutexAllow other threads to lockmtx.unlock();
RAII lockAutomatically lock/unlock with scopestd::lock_guard lock(mtx);
Try lockAttempt to lock without blockingif (mtx.try_lock()) { /*...*/ mtx.unlock(); }

Key Takeaways

Use std::mutex to protect shared data in multithreaded C++ programs.
Prefer std::lock_guard to automatically manage locking and unlocking.
Always unlock mutexes to avoid deadlocks; lock_guard helps prevent this mistake.
Lock the mutex only for the shortest time needed to keep concurrency high.
Avoid accessing shared data without locking to prevent race conditions.