Windows 原生同步物件(Critical Section、Mutex、Event、Semaphore)雖強大,但需手動配對呼叫 Enter/Leave 或 Wait/Signal,一旦函數中途 return、拋出異常或 goto 跳轉,極易造成死鎖或資源洩漏。C# lock(obj)、Java synchronized(obj) 以語法糖保證「進入區塊即加鎖、離開區塊必解鎖」。本文展示如何在 C++ 中用 RAII (Resource Acquisition Is Initialization) 模式實現同等體驗。
// Mutex.h
#pragma once
#include
class Mutex {
public:
Mutex() { InitializeCriticalSection(&m_cs); }
~Mutex() { DeleteCriticalSection(&m_cs); }
void Lock() { EnterCriticalSection(&m_cs); }
void Unlock() { LeaveCriticalSection(&m_cs); }
// 禁止拷貝/移動,避免雙重釋放
Mutex(const Mutex&) = delete;
Mutex& operator=(const Mutex&) = delete;
private:
CRITICAL_SECTION m_cs;
};
Critical Section 輕量、用戶態、適合進程內執行緒同步;跨進程請改用 CreateMutex 封裝。
// Mutex.h (續)
class Lock {
public:
explicit Lock(Mutex& m) : m_mutex(m), m_locked(true) {
m_mutex.Lock();
}
~Lock() {
if (m_locked) m_mutex.Unlock(); // 析構自動解鎖,異常安全
}
// 支援 synchronized 宏所需的介面
void SetUnlock() { m_locked = false; } // 提前釋放鎖
operator bool() const { return m_locked; } // for 迴圈條件判斷
private:
Mutex& m_mutex;
bool m_locked;
Lock(const Lock&) = delete;
Lock& operator=(const Lock&) = delete;
};
#define synchronized(M) \
for (Lock M##_lock = M; M##_lock; M##_lock.SetUnlock())
原理:for(init; condition; increment) 只執行一次迴圈體。
init 建立 Lock 物件並加鎖;
condition 呼叫 operator bool() 返回 true 進入迴圈體;
迴圈體結束執行 increment → SetUnlock() 標記已解鎖;
下一次 condition 為 false 退出迴圈,Lock 析構(若未提前解鎖則再次解鎖,雙重保險)。
Mutex g_dataMutex;
std::vector g_sharedData;
void ThreadSafePush(int val) {
synchronized(g_dataMutex) {
g_sharedData.push_back(val); // 區塊結束自動解鎖
if (val < 0) throw std::runtime_error("negative"); // 異常也安全
}
}
int ThreadSafePop() {
synchronized(g_dataMutex) {
if (g_sharedData.empty()) return -1;
int v = g_sharedData.back();
g_sharedData.pop_back();
return v; // 正常返回自動解鎖
}
}
SRWLOCK,可封裝 SharedLock/ExclusiveLock。Mutex 核心物件 + WaitForSingleObject,RAII 包裝時在建構函數處理超時邏輯。以 Mutex 封裝底層同步物件、Lock 實現 RAII、macro 提供語法糖 三層架構,僅 50 行核心代碼即可在 C++ 中獲得 C#/Java 級別的鎖使用體驗:簡潔、異常安全、零洩漏。這正是「現代 C++ 風格」在舊代碼庫中的實用落地。