CodeNebula
C++11coreintermediate

std::mutex (C++11 Addition)

C++11 · Modern Revolution · Compiler flag: -std=c++11

Explanation

A synchronization primitive that provides mutual exclusion, ensuring only one thread accesses a shared resource at a time.

Real-World Scenario

Protect a shared log file from concurrent writes in a multi-threaded server.

Quick Snippet

snippet.cpp
#include <mutex>
std::mutex mtx;
void safeWrite(const std::string& msg) {
    mtx.lock();
    // write to shared resource
    mtx.unlock();
}

Full Example

mutex.cpp
// CodeNebula - C++11 Example: std::mutex
// Compile: g++ -std=c++11 -Wall -Wextra -pthread -o example mutex.cpp
//
// This example demonstrates:
// - std::mutex for mutual exclusion
// - lock() and unlock() basic usage
// - try_lock() for non-blocking lock attempts
// - std::recursive_mutex for reentrant locking
// - Data races without mutex vs. correct code with mutex

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <string>

// ============================================================
// 1. Shared counter without protection (data race)
// ============================================================
int unsafeCounter = 0;

void unsafeIncrement(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        ++unsafeCounter;  // DATA RACE!
    }
}

// ============================================================
// 2. Shared counter with std::mutex
// ============================================================
int safeCounter = 0;
std::mutex counterMutex;

void safeIncrement(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        counterMutex.lock();
        ++safeCounter;
        counterMutex.unlock();
    }
}

// ============================================================
// 3. try_lock() for non-blocking attempts
// ============================================================
std::mutex resourceMutex;

void tryAccessResource(const std::string& name) {
    if (resourceMutex.try_lock()) {
        std::cout << "  " << name << ": acquired lock, working..." << std::endl;
        // Simulate work
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        resourceMutex.unlock();
        std::cout << "  " << name << ": released lock" << std::endl;
    } else {
        std::cout << "  " << name << ": lock busy, doing other work" << std::endl;
    }
}

// ============================================================
// 4. std::recursive_mutex for reentrant locking
// ============================================================
class TreeNode {
    std::string name_;
    std::vector<TreeNode*> children_;
    mutable std::recursive_mutex mtx_;
public:
    TreeNode(const std::string& name) : name_(name) {}

    void addChild(TreeNode* child) {
        std::lock_guard<std::recursive_mutex> lock(mtx_);
        children_.push_back(child);
    }

    void print(int depth = 0) const {
        std::lock_guard<std::recursive_mutex> lock(mtx_);
        std::cout << std::string(depth * 2, ' ') << "  " << name_ << std::endl;
        for (const auto* child : children_) {
            child->print(depth + 1);  // Recursive: re-locks the mutex
        }
    }
};

int main() {
    std::cout << "=== std::mutex ===" << std::endl;

    // Unsafe (data race) demonstration
    std::cout << "\n--- Without mutex (data race) ---" << std::endl;
    {
        std::vector<std::thread> threads;
        for (int i = 0; i < 4; ++i) {
            threads.emplace_back(unsafeIncrement, 10000);
        }
        for (auto& t : threads) t.join();
        std::cout << "  Expected: 40000, Got: " << unsafeCounter
                  << (unsafeCounter != 40000 ? " (race detected!)" : "") << std::endl;
    }

    // Safe (mutex-protected) demonstration
    std::cout << "\n--- With std::mutex ---" << std::endl;
    {
        std::vector<std::thread> threads;
        for (int i = 0; i < 4; ++i) {
            threads.emplace_back(safeIncrement, 10000);
        }
        for (auto& t : threads) t.join();
        std::cout << "  Expected: 40000, Got: " << safeCounter << std::endl;
    }

    // try_lock demonstration
    std::cout << "\n--- try_lock() ---" << std::endl;
    {
        std::thread t1(tryAccessResource, "Thread-A");
        std::thread t2(tryAccessResource, "Thread-B");
        t1.join();
        t2.join();
    }

    // Recursive mutex demonstration
    std::cout << "\n--- recursive_mutex (tree) ---" << std::endl;
    {
        TreeNode root("root");
        TreeNode child1("child1");
        TreeNode child2("child2");
        TreeNode grandchild("grandchild");

        root.addChild(&child1);
        root.addChild(&child2);
        child1.addChild(&grandchild);

        root.print();
    }

    return 0;
}

// Expected output (thread interleaving and data race results may vary):
// === std::mutex ===
//
// --- Without mutex (data race) ---
//   Expected: 40000, Got: 38542 (race detected!)
//
// --- With std::mutex ---
//   Expected: 40000, Got: 40000
//
// --- try_lock() ---
//   Thread-A: acquired lock, working...
//   Thread-B: lock busy, doing other work
//   Thread-A: released lock
//
// --- recursive_mutex (tree) ---
//   root
//     child1
//       grandchild
//     child2
Run command
g++ -std=c++11 -Wall -Wextra -pthread -o example mutex.cpp