多執行緒共享進程資源(全域變數、檔案、裝置等),若無協調機制,會發生競爭條件:
MFC 提供四大同步物件,均衍生自 CSyncObject:
| 類別 | 適用場景 | 跨進程 | 效能 |
|---|---|---|---|
| CCriticalSection | 單一進程內保護共享資源/代碼段 | 否 | 最高(核心模式切換少) |
| CMutex | 跨進程互斥存取 | 是 | 較低(核心物件開銷大) |
| CEvent | 執行緒間通知/等待特定條件 | 是 | 中等 |
| CSemaphore | 限制同時存取資源的執行緒數量 | 是 | 中等 |
同一時間僅允許一執行緒進入臨界區。
// 全域宣告(確保各執行緒可見)
CCriticalSection g_cs;
char g_Array[10];
UINT WriteW(LPVOID pParam) {
CEdit* pEdit = (CEdit*)pParam;
pEdit->SetWindowText(_T(""));
g_cs.Lock(); // 獲得鎖,若被佔用則阻塞等待
for (int i=0; i<10; i++) {
g_Array[i] = 'W';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
g_cs.Unlock(); // 釋放鎖
return 0;
}
// WriteD 同理,將 'W' 改為 'D'
關鍵行為:執行緒 A 執行到 Lock() 時,若執行緒 B 正在臨界區內(已 Lock 尚未 Unlock),A 會阻塞等待直到 B 呼叫 Unlock()。
類似「旗標」,一執行緒設定訊號,另一執行緒等待訊號。
// 全域事件物件,預設自動重置、無訊號狀態
CEvent g_eventWriteD;
char g_Array[10];
UINT WriteW(LPVOID pParam) {
CEdit* pEdit = (CEdit*)pParam;
pEdit->SetWindowText(_T(""));
for (int i=0; i<10; i++) {
g_Array[i] = 'W';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
g_eventWriteD.SetEvent(); // 發送訊號,喚醒等待執行緒
return 0;
}
UINT WriteD(LPVOID pParam) {
CEdit* pEdit = (CEdit*)pParam;
pEdit->SetWindowText(_T(""));
// 等待訊號,INFINITE 表示無限期等待
WaitForSingleObject(g_eventWriteD.m_hObject, INFINITE);
for (int i=0; i<10; i++) {
g_Array[i] = 'D';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
return 0;
}
自動 vs 手動事件:
SetEvent() 釋放**一個**等待執行緒後自動重置為無訊號SetEvent() 後保持有訊號,需呼叫 ResetEvent() 手動重置用法與 CCriticalSection 類似(Lock()/Unlock()),但可跨進程使用。同進程內優先用 CCriticalSection 效能更好。
控制同時存取資源的執行緒上限。建構時指定最大計數與初始可用計數。
// 最多 2 執行緒同時存取,初始可用 2
CSemaphore g_sem(2, 2);
char g_Array[10];
UINT WriteA(LPVOID pParam) {
CEdit* pEdit = (CEdit*)pParam;
pEdit->SetWindowText(_T(""));
WaitForSingleObject(g_sem.m_hObject, INFINITE); // 計數 -1
CString str;
for (int i=0; i<10; i++) {
pEdit->GetWindowText(str);
g_Array[i] = 'A';
str += g_Array[i];
pEdit->SetWindowText(str);
Sleep(1000);
}
ReleaseSemaphore(g_sem.m_hObject, 1, NULL); // 計數 +1
return 0;
}
// WriteB, WriteC 同理
三個執行緒同時啟動時,只有兩個能進入迴圈,第三個會在 WaitForSingleObject 阻塞,直到前釋放信號量。
CCriticalSection 保護共用陣列CEvent 實現寫 W 完成後通知寫 DCSemaphore(2,2) 限制同時寫入執行緒數為 2CCriticalSection(最輕量)CMutex 或具名 CEvent/CSemaphoreCEventCSemaphoreLock/Unlock、Wait/Release,建議用 RAII 包裝避免洩漏掌握四大同步物件特性與適用場景,是編寫穩健多執行緒程式的基石。實務上常組合使用(如 Critical Section 保護佇列 + Event 通知有新任務),靈活運用才能事半功倍。