S SmartDocs
Serie: C++ cpp 488 líneas · Actualizado 2026-04-03

mutex_lock.cpp

C++/Part5_進階主題/Ch23_多執行緒程式設計/mutex_lock.cpp

// mutex_lock.cpp
// 編譯指令:g++ -std=c++17 -Wall -pthread mutex_lock.cpp -o mutex_lock
//
// 注意:多執行緒程式必須加上 -pthread 旗標
//
// 本程式示範 mutex、各種鎖定機制、condition_variable 和 atomic

#include <iostream>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <atomic>
#include <vector>
#include <queue>
#include <string>
#include <chrono>
#include <functional>
#include <numeric>

std::mutex cout_mutex;

template<typename... Args>
void safe_print(const Args&... args) {
    std::lock_guard<std::mutex> lock(cout_mutex);
    (std::cout << ... << args) << "\n";
}

// ============================================================
// 第一部分:資料競爭問題展示
// ============================================================

void demo_data_race() {
    std::cout << "========================================\n";
    std::cout << "  資料競爭問題展示\n";
    std::cout << "========================================\n\n";

    // 沒有保護的計數器 — 會產生資料競爭
    int unsafe_counter = 0;
    const int iterations = 100000;

    {
        std::vector<std::thread> threads;
        for (int t = 0; t < 4; ++t) {
            threads.emplace_back([&unsafe_counter]() {
                for (int i = 0; i < 100000; ++i) {
                    ++unsafe_counter;  // 資料競爭!
                }
            });
        }
        for (auto& t : threads) t.join();
    }

    std::cout << "未保護的計數器(4 執行緒各加 " << iterations << " 次):\n";
    std::cout << "  預期值: " << 4 * iterations << "\n";
    std::cout << "  實際值: " << unsafe_counter
              << (unsafe_counter == 4 * iterations ? " (碰巧正確)" : " (資料競爭!)")
              << "\n\n";

    // 使用 mutex 保護的計數器
    int safe_counter = 0;
    std::mutex counter_mutex;

    {
        std::vector<std::thread> threads;
        for (int t = 0; t < 4; ++t) {
            threads.emplace_back([&safe_counter, &counter_mutex]() {
                for (int i = 0; i < 100000; ++i) {
                    std::lock_guard<std::mutex> lock(counter_mutex);
                    ++safe_counter;
                }
            });
        }
        for (auto& t : threads) t.join();
    }

    std::cout << "mutex 保護的計數器:\n";
    std::cout << "  預期值: " << 4 * iterations << "\n";
    std::cout << "  實際值: " << safe_counter << " ✓\n\n";
}

// ============================================================
// 第二部分:std::lock_guard(RAII 鎖定)
// ============================================================

class BankAccount {
    mutable std::mutex mtx_;
    double balance_;
    std::string name_;

public:
    BankAccount(const std::string& name, double balance)
        : balance_(balance), name_(name) {}

    void deposit(double amount) {
        std::lock_guard<std::mutex> lock(mtx_);
        balance_ += amount;
        safe_print("  ", name_, " 存入 ", amount, ", 餘額: ", balance_);
    }

    bool withdraw(double amount) {
        std::lock_guard<std::mutex> lock(mtx_);
        if (balance_ >= amount) {
            balance_ -= amount;
            safe_print("  ", name_, " 提出 ", amount, ", 餘額: ", balance_);
            return true;
        }
        safe_print("  ", name_, " 餘額不足!無法提出 ", amount);
        return false;
    }

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

    const std::string& name() const { return name_; }
};

void demo_lock_guard() {
    std::cout << "========================================\n";
    std::cout << "  std::lock_guard(RAII 鎖定)\n";
    std::cout << "========================================\n\n";

    BankAccount account("儲蓄帳戶", 1000.0);
    std::cout << "初始餘額: " << account.get_balance() << "\n\n";

    std::vector<std::thread> threads;

    // 多個執行緒同時存取帳戶
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back([&account, i]() {
            account.deposit(100.0 * (i + 1));
        });
        threads.emplace_back([&account, i]() {
            account.withdraw(50.0 * (i + 1));
        });
    }

    for (auto& t : threads) t.join();

    std::cout << "\n最終餘額: " << account.get_balance() << "\n";
    // 預期:1000 + (100+200+300+400+500) - (50+100+150+200+250) = 1750
    std::cout << "預期餘額: 1750\n\n";
}

// ============================================================
// 第三部分:std::unique_lock(彈性鎖定)
// ============================================================

void demo_unique_lock() {
    std::cout << "========================================\n";
    std::cout << "  std::unique_lock(彈性鎖定)\n";
    std::cout << "========================================\n\n";

    std::mutex mtx;
    int shared_data = 0;

    // 基本用法(同 lock_guard)
    std::cout << "1. 基本用法:\n";
    {
        std::unique_lock<std::mutex> lock(mtx);
        shared_data = 42;
        std::cout << "  已鎖定,修改 shared_data = " << shared_data << "\n";
    }

    // defer_lock:延遲鎖定
    std::cout << "\n2. defer_lock(延遲鎖定):\n";
    {
        std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
        std::cout << "  建立 unique_lock 但未鎖定\n";
        std::cout << "  owns_lock: " << std::boolalpha << lock.owns_lock() << "\n";
        lock.lock();
        std::cout << "  手動鎖定後 owns_lock: " << lock.owns_lock() << "\n";
        shared_data = 100;
    }

    // 手動解鎖/重新鎖定
    std::cout << "\n3. 手動解鎖/重新鎖定:\n";
    {
        std::unique_lock<std::mutex> lock(mtx);
        std::cout << "  鎖定中:執行臨界區操作...\n";

        lock.unlock();
        std::cout << "  已解鎖:執行不需要鎖的操作...\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(10));

        lock.lock();
        std::cout << "  重新鎖定:繼續臨界區操作...\n";
    }

    // try_lock
    std::cout << "\n4. try_lock(嘗試鎖定):\n";
    {
        std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
        if (lock.try_lock()) {
            std::cout << "  成功取得鎖!\n";
        } else {
            std::cout << "  無法取得鎖\n";
        }
    }

    std::cout << "\n";
}

// ============================================================
// 第四部分:std::scoped_lock(C++17 同時鎖多個 mutex)
// ============================================================

class Account {
    std::mutex mtx_;
    double balance_;
    std::string name_;

public:
    Account(const std::string& name, double bal)
        : balance_(bal), name_(name) {}

    // 允許 friend 存取 mutex
    friend void transfer(Account& from, Account& to, double amount);
    double balance() const { return balance_; }
    const std::string& name() const { return name_; }
};

void transfer(Account& from, Account& to, double amount) {
    // scoped_lock 同時鎖定兩個 mutex,避免死鎖
    std::scoped_lock lock(from.mtx_, to.mtx_);

    if (from.balance_ >= amount) {
        from.balance_ -= amount;
        to.balance_ += amount;
        safe_print("  轉帳 ", amount, ": ", from.name_, " -> ", to.name_,
                   " (餘額: ", from.balance_, " / ", to.balance_, ")");
    } else {
        safe_print("  轉帳失敗!", from.name_, " 餘額不足");
    }
}

void demo_scoped_lock() {
    std::cout << "========================================\n";
    std::cout << "  std::scoped_lock(C++17)\n";
    std::cout << "========================================\n\n";

    Account alice("Alice", 1000);
    Account bob("Bob", 1000);

    std::cout << "初始餘額: Alice=" << alice.balance()
              << ", Bob=" << bob.balance() << "\n\n";

    // 兩個方向的轉帳同時進行 — 使用 scoped_lock 避免死鎖
    std::vector<std::thread> threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(transfer, std::ref(alice), std::ref(bob), 100.0);
        threads.emplace_back(transfer, std::ref(bob), std::ref(alice), 100.0);
    }

    for (auto& t : threads) t.join();

    std::cout << "\n最終餘額: Alice=" << alice.balance()
              << ", Bob=" << bob.balance() << "\n";
    std::cout << "總額: " << (alice.balance() + bob.balance())
              << " (應保持 2000)\n\n";
}

// ============================================================
// 第五部分:std::condition_variable(生產者-消費者)
// ============================================================

template<typename T>
class ThreadSafeQueue {
    std::queue<T> queue_;
    mutable std::mutex mutex_;
    std::condition_variable cv_not_empty_;
    std::condition_variable cv_not_full_;
    std::size_t max_size_;

public:
    explicit ThreadSafeQueue(std::size_t max_size = 10) : max_size_(max_size) {}

    void push(T item) {
        std::unique_lock<std::mutex> lock(mutex_);
        cv_not_full_.wait(lock, [this]() { return queue_.size() < max_size_; });
        queue_.push(std::move(item));
        cv_not_empty_.notify_one();
    }

    T pop() {
        std::unique_lock<std::mutex> lock(mutex_);
        cv_not_empty_.wait(lock, [this]() { return !queue_.empty(); });
        T item = std::move(queue_.front());
        queue_.pop();
        cv_not_full_.notify_one();
        return item;
    }

    std::size_t size() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return queue_.size();
    }
};

void demo_condition_variable() {
    std::cout << "========================================\n";
    std::cout << "  std::condition_variable 生產者-消費者\n";
    std::cout << "========================================\n\n";

    ThreadSafeQueue<int> queue(5);  // 最大容量 5
    std::atomic<bool> done{false};
    const int total_items = 20;

    // 生產者
    std::thread producer([&]() {
        for (int i = 1; i <= total_items; ++i) {
            queue.push(i);
            safe_print("  [生產者] 放入: ", i, " (佇列大小: ", queue.size(), ")");
            std::this_thread::sleep_for(std::chrono::milliseconds(20));
        }
        done.store(true);
    });

    // 消費者 1
    std::thread consumer1([&]() {
        while (!done.load() || queue.size() > 0) {
            // 使用簡單的檢查避免在佇列空且完成時卡住
            if (queue.size() > 0) {
                int item = queue.pop();
                safe_print("  [消費者1] 取出: ", item);
                std::this_thread::sleep_for(std::chrono::milliseconds(50));
            } else {
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        }
    });

    // 消費者 2
    std::thread consumer2([&]() {
        while (!done.load() || queue.size() > 0) {
            if (queue.size() > 0) {
                int item = queue.pop();
                safe_print("  [消費者2] 取出: ", item);
                std::this_thread::sleep_for(std::chrono::milliseconds(50));
            } else {
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        }
    });

    producer.join();
    // 給消費者時間處理剩餘項目
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    consumer1.join();
    consumer2.join();

    std::cout << "\n生產者-消費者模式完成!\n\n";
}

// ============================================================
// 第六部分:std::atomic 基礎
// ============================================================

void demo_atomic() {
    std::cout << "========================================\n";
    std::cout << "  std::atomic 原子操作\n";
    std::cout << "========================================\n\n";

    const int iterations = 100000;

    // atomic 計數器 — 不需要 mutex
    std::atomic<int> atomic_counter{0};
    {
        std::vector<std::thread> threads;
        for (int t = 0; t < 4; ++t) {
            threads.emplace_back([&atomic_counter]() {
                for (int i = 0; i < 100000; ++i) {
                    atomic_counter.fetch_add(1, std::memory_order_relaxed);
                }
            });
        }
        for (auto& t : threads) t.join();
    }

    std::cout << "atomic 計數器(4 執行緒各加 " << iterations << " 次):\n";
    std::cout << "  預期值: " << 4 * iterations << "\n";
    std::cout << "  實際值: " << atomic_counter.load() << " ✓\n\n";

    // atomic 操作示範
    std::atomic<int> val{10};
    std::cout << "atomic 操作示範:\n";
    std::cout << "  初始值: " << val.load() << "\n";

    val.store(20);
    std::cout << "  store(20): " << val.load() << "\n";

    int old = val.fetch_add(5);
    std::cout << "  fetch_add(5): 舊值=" << old << ", 新值=" << val.load() << "\n";

    old = val.fetch_sub(3);
    std::cout << "  fetch_sub(3): 舊值=" << old << ", 新值=" << val.load() << "\n";

    int expected = 22;
    bool success = val.compare_exchange_strong(expected, 100);
    std::cout << "  compare_exchange(22, 100): "
              << (success ? "成功" : "失敗")
              << ", 值=" << val.load() << "\n";

    // atomic_flag(最輕量的 atomic)
    std::cout << "\natomic_flag(自旋鎖概念):\n";
    std::atomic_flag spinlock = ATOMIC_FLAG_INIT;

    auto try_acquire = [&spinlock](int id) {
        while (spinlock.test_and_set(std::memory_order_acquire)) {
            std::this_thread::yield();
        }
        safe_print("  Thread ", id, " 取得鎖");
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        spinlock.clear(std::memory_order_release);
        safe_print("  Thread ", id, " 釋放鎖");
    };

    std::vector<std::thread> spin_threads;
    for (int i = 0; i < 3; ++i) {
        spin_threads.emplace_back(try_acquire, i);
    }
    for (auto& t : spin_threads) t.join();

    std::cout << "\n";
}

// ============================================================
// 第七部分:簡易 notify/wait 模式
// ============================================================

void demo_simple_notification() {
    std::cout << "========================================\n";
    std::cout << "  條件變數通知模式\n";
    std::cout << "========================================\n\n";

    std::mutex mtx;
    std::condition_variable cv;
    bool data_ready = false;
    std::string shared_data;

    // 工作執行緒(準備資料)
    std::thread worker([&]() {
        safe_print("  [工作者] 開始準備資料...");
        std::this_thread::sleep_for(std::chrono::milliseconds(200));

        {
            std::lock_guard<std::mutex> lock(mtx);
            shared_data = "重要的計算結果: 42";
            data_ready = true;
        }
        cv.notify_one();
        safe_print("  [工作者] 資料已準備好,已通知");
    });

    // 主執行緒(等待資料)
    {
        std::unique_lock<std::mutex> lock(mtx);
        safe_print("  [主程式] 等待資料...");
        cv.wait(lock, [&]() { return data_ready; });
        safe_print("  [主程式] 收到資料: \"", shared_data, "\"");
    }

    worker.join();
    std::cout << "\n";
}

// ============================================================
// 主程式
// ============================================================

int main() {
    std::cout << "╔══════════════════════════════════════════╗\n";
    std::cout << "║  C++17 mutex、鎖定機制與原子操作          ║\n";
    std::cout << "╚══════════════════════════════════════════╝\n\n";

    demo_data_race();
    demo_lock_guard();
    demo_unique_lock();
    demo_scoped_lock();
    demo_condition_variable();
    demo_atomic();
    demo_simple_notification();

    std::cout << "=== 程式結束 ===\n";
    return 0;
}

Artículos relacionados