CodeNebula
C++11coreintermediate

std::lock_guard (C++11 Addition)

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

Explanation

A RAII wrapper that acquires a mutex on construction and releases it on destruction, guaranteeing the mutex is always released even if an exception occurs.

Real-World Scenario

Safely update a shared counter in a multi-threaded hit counter without risking deadlocks from forgotten unlocks.

Quick Snippet

snippet.cpp
#include <mutex>
std::mutex mtx;
void increment(int& counter) {
    std::lock_guard<std::mutex> lock(mtx);
    ++counter; // mutex released when lock goes out of scope
}

Full Example

lock_guard.cpp
// CodeNebula - C++11 Example: std::lock_guard
// Compile: g++ -std=c++11 -Wall -Wextra -pthread -o example lock_guard.cpp
//
// This example demonstrates:
// - std::lock_guard for RAII-based mutex locking
// - Automatic unlock on scope exit (even with exceptions)
// - Comparison with manual lock/unlock
// - Using lock_guard with different mutex types

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

// ============================================================
// 1. Basic lock_guard usage
// ============================================================
class BankAccount {
    mutable std::mutex mtx_;
    std::string owner_;
    double balance_;

public:
    BankAccount(const std::string& owner, double balance)
        : owner_(owner), balance_(balance) {}

    void deposit(double amount) {
        std::lock_guard<std::mutex> lock(mtx_);  // Locks on construction
        balance_ += amount;
        std::cout << "  " << owner_ << ": deposited " << amount
                  << ", balance = " << balance_ << std::endl;
    }  // Automatically unlocks when lock goes out of scope

    bool withdraw(double amount) {
        std::lock_guard<std::mutex> lock(mtx_);
        if (balance_ >= amount) {
            balance_ -= amount;
            std::cout << "  " << owner_ << ": withdrew " << amount
                      << ", balance = " << balance_ << std::endl;
            return true;
        }
        std::cout << "  " << owner_ << ": insufficient funds" << std::endl;
        return false;
    }

    double getBalance() const {
        std::lock_guard<std::mutex> lock(mtx_);
        return balance_;
    }
};

// ============================================================
// 2. Exception safety with lock_guard
// ============================================================
std::mutex dataMutex;

void riskyOperation(int value) {
    std::lock_guard<std::mutex> lock(dataMutex);
    std::cout << "  Processing: " << value << std::endl;

    if (value < 0) {
        // lock_guard releases mutex even when exception is thrown
        throw std::runtime_error("Negative value not allowed");
    }

    std::cout << "  Completed: " << value << std::endl;
    // Mutex released here automatically
}

// ============================================================
// 3. Scoped locking for fine-grained control
// ============================================================
class ThreadSafeLog {
    std::mutex mtx_;
    std::vector<std::string> entries_;

public:
    void add(const std::string& entry) {
        // Only lock for the critical section
        {
            std::lock_guard<std::mutex> lock(mtx_);
            entries_.push_back(entry);
        }
        // Mutex already released; do non-critical work here
    }

    void printAll() const {
        // Cast away const for mutex (or use mutable)
        auto& self = const_cast<ThreadSafeLog&>(*this);
        std::lock_guard<std::mutex> lock(self.mtx_);
        std::cout << "  Log entries (" << entries_.size() << "):" << std::endl;
        for (const auto& e : entries_) {
            std::cout << "    - " << e << std::endl;
        }
    }
};

// ============================================================
// 4. adopt_lock: lock_guard with already-locked mutex
// ============================================================
std::mutex sharedMutex;

void adoptLockDemo() {
    sharedMutex.lock();  // Manually lock first
    // Tell lock_guard the mutex is already locked
    std::lock_guard<std::mutex> lock(sharedMutex, std::adopt_lock);
    std::cout << "  adopt_lock: mutex was pre-locked, guard manages unlock" << std::endl;
}  // Guard releases the mutex

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

    // Bank account with thread-safe operations
    std::cout << "\n--- Thread-safe bank account ---" << std::endl;
    BankAccount account("Alice", 1000.0);

    std::vector<std::thread> threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(&BankAccount::deposit, &account, 100.0);
    }
    for (auto& t : threads) t.join();
    std::cout << "  Final balance: " << account.getBalance() << std::endl;

    // Exception safety
    std::cout << "\n--- Exception safety ---" << std::endl;
    try {
        riskyOperation(10);
        riskyOperation(-1);   // Throws, but mutex is safely released
    } catch (const std::runtime_error& e) {
        std::cout << "  Caught: " << e.what() << std::endl;
    }
    // Mutex is available again after exception
    riskyOperation(20);

    // Thread-safe log
    std::cout << "\n--- Thread-safe log ---" << std::endl;
    ThreadSafeLog log;
    std::vector<std::thread> logThreads;
    for (int i = 0; i < 4; ++i) {
        logThreads.emplace_back([&log, i]() {
            log.add("Entry from thread " + std::to_string(i));
        });
    }
    for (auto& t : logThreads) t.join();
    log.printAll();

    // adopt_lock
    std::cout << "\n--- adopt_lock ---" << std::endl;
    adoptLockDemo();

    return 0;
}

// Expected output (thread ordering may vary):
// === std::lock_guard ===
//
// --- Thread-safe bank account ---
//   Alice: deposited 100, balance = 1100
//   Alice: deposited 100, balance = 1200
//   Alice: deposited 100, balance = 1300
//   Alice: deposited 100, balance = 1400
//   Alice: deposited 100, balance = 1500
//   Final balance: 1500
//
// --- Exception safety ---
//   Processing: 10
//   Completed: 10
//   Processing: -1
//   Caught: Negative value not allowed
//   Processing: 20
//   Completed: 20
//
// --- Thread-safe log ---
//   Log entries (4):
//     - Entry from thread 0
//     - Entry from thread 1
//     - Entry from thread 2
//     - Entry from thread 3
//
// --- adopt_lock ---
//   adopt_lock: mutex was pre-locked, guard manages unlock
Run command
g++ -std=c++11 -Wall -Wextra -pthread -o example lock_guard.cpp