Serie: C++
cpp
488 righe
· Aggiornato 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;
}
Articoli correlati
C++
c
Aggiornato 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
Leggi l'articolo →
C++
c
Aggiornato 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
Leggi l'articolo →
C++
cpp
Aggiornato 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
Leggi l'articolo →
C++
c
Aggiornato 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
Leggi l'articolo →
C++
c
Aggiornato 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
Leggi l'articolo →
C++
cpp
Aggiornato 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
Leggi l'articolo →