前言
執行緒是進程內的執行單元,擁有獨立堆疊、暫存器狀態,共享進程所有資源 (檔案控制代碼、記憶體、全域變數)。Windows 透過排程器將 CPU 時間片輪流分配給各執行緒,形成「同時運行」假象。本文系統梳理 **Win32 API** 與 **MFC** 兩大開發體系的建立、同步、終止全流程。
一、Win32 API 體系:最接近核心的控制權
1. 建立與終止執行緒
// 建立
HANDLE hThread = CreateThread(
NULL, // 安全性屬性
0, // 堆疊大小 (0=預設 1MB)
ThreadFunc, // 進入點函式
pParam, // 傳遞參數
0, // 建立旗標 (CREATE_SUSPENDED=暫緩啟動)
&dwThreadId // 接收執行緒 ID
);
// 進入點原型
DWORD WINAPI ThreadFunc(LPVOID pParam) { ... return 0; }
// 終止方式
// 內部正常返回 → 自動結束
ExitThread(dwExitCode); // 執行緒自我結束
TerminateThread(hThread, dwExitCode); // **外部強制終止,極度不推薦** (資源洩漏、鎖死、堆疊損毀)
2. 執行緒同步四大核心物件
| 物件 | 適用場景 | 關鍵 API | 特點 |
| Mutex (互斥體) | 跨進程/執行緒獨佔資源 | CreateMutex, WaitForSingleObject, ReleaseMutex | 核心物件、可命名跨進程、擁有權機制 (避免遺棄) |
| Semaphore (訊號) | 限制同時存取資源的執行緒數 (連線池、佇列槽位) | CreateSemaphore, WaitForSingleObject, ReleaseSemaphore | 計數器模型、初始/最大計數可設定 |
| Event (事件) | 執行緒間通知/等待條件 (資料就緒、關機訊號) | CreateEvent, SetEvent, ResetEvent, WaitForSingleObject | 手動/自動重置、可命名跨進程 |
| Critical Section (臨界區) | **同進程內** 高效能互斥 | InitializeCriticalSection, Enter/TryEnter/LeaveCriticalSection | **用戶模式**、無核心切換開銷、不可跨進程、支援自旋計數 |
3. 等待函式:同步的心跳
// 等單一物件
DWORD WaitForSingleObject(HANDLE h, DWORD dwMilliseconds); // INFINITE=永久等待
// 等多個物件
DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE* lpHandles,
BOOL bWaitAll, DWORD dwMilliseconds);
// bWaitAll=TRUE 全部觸發才返回;FALSE 任一觸發即返回
4. 經典範例:生產者-消費者 (Mutex + 兩個 Event)
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
HANDLE hNotFull = CreateEvent(NULL, FALSE, TRUE, NULL); // 初始有空槽
HANDLE hNotEmpty = CreateEvent(NULL, FALSE, FALSE, NULL); // 初始無資料
// Producer
WaitForSingleObject(hNotFull, INFINITE);
WaitForSingleObject(hMutex, INFINITE);
// ... 寫入共享緩衝區 ...
ReleaseMutex(hMutex);
SetEvent(hNotEmpty);
// Consumer
WaitForSingleObject(hNotEmpty, INFINITE);
WaitForSingleObject(hMutex, INFINITE);
// ... 讀取共享緩衝區 ...
ReleaseMutex(hMutex);
SetEvent(hNotFull);
二、MFC 體系:封裝之美,生產力躍升
1. 兩種執行緒類型
- 工作者執行緒:無訊息迴路,純計算/IO 任務,對應
AfxBeginThread(ThreadFunc, param)
- 使用者介面執行緒:擁有訊息泵、視窗、可處理 UI,需衍生
CWinThread
2. 工作者執行緒建立與控制
UINT __cdecl MyThreadFunc(LPVOID pParam) {
// pParam 可傳遞結構體指標
for (int i=0; i<100; ++i) {
if (::WaitForSingleObject(g_hStopEvent, 0) == WAIT_OBJECT_0) break;
// 做工作...
Sleep(50);
}
return 0;
}
// 啟動
CWinThread* pThread = AfxBeginThread(MyThreadFunc, pData,
THREAD_PRIORITY_NORMAL, 0, 0, NULL);
// 掛起/恢復
pThread->SuspendThread(); pThread->ResumeThread();
// 等待結束
WaitForSingleObject(pThread->m_hThread, INFINITE);
3. UI 執行緒建立 (衍生 CWinThread)
- ClassWizard → Add Class → Base:
CWinThread → 名稱 CMyUIThread
- 必須保留
DECLARE_DYNCREATE / IMPLEMENT_DYNCREATE
- 覆寫
InitInstance() (建立視窗) 與 ExitInstance() (清理)
- 啟動:
AfxBeginThread(RUNTIME_CLASS(CMyUIThread)) 或兩階段 new CMyUIThread; pThread->CreateThread()
4. MFC 同步類別:RAII 風格,拒絕洩漏
| Win32 物件 | MFC 封裝 | 用法範例 |
| Mutex | CMutex | CMutex mut; CSingleLock lock(&mut); lock.Lock(); ... lock.Unlock(); |
| Semaphore | CSemaphore | 同上,CSingleLock 通用 |
| Event | CEvent | 同上 |
| CriticalSection | CCriticalSection | CCriticalSection cs; CSingleLock lock(&cs); lock.Lock(); ... |
多物件等待:CMultiLock lock(arr, count); lock.Lock(INFINITE, FALSE); // 任一觸發 |
5. 範例:CCriticalSection 保護共享佇列
class CTaskQueue {
std::queue q;
CCriticalSection cs;
public:
void Push(const Task& t) {
CSingleLock lock(&cs); lock.Lock();
q.push(t); lock.Unlock();
}
bool Pop(Task& t) {
CSingleLock lock(&cs); lock.Lock();
if (q.empty()) return false;
t = q.front(); q.pop(); return true;
}
};
三、進階主題與避坑指南
| 主題 | 關鍵點 |
| 執行緒區域儲存 (TLS) | TlsAlloc/SetValue/GetValue/Free;MFC: CWinThread::m_threadLocalData (內部使用) |
| 執行緒優先權 | SetThreadPriority(h, THREAD_PRIORITY_HIGHEST/LOWEST/IDLE);動態調整避免飢餓 |
| 非同步 I/O (IOCP) | CreateFile(FILE_FLAG_OVERLAPPED) + CreateIoCompletionPort,高併發網路/檔案伺服器標配 |
| 執行緒池 | Win32: QueueUserWorkItem / CreateThreadpoolWork (Vista+);避免頻繁建銷開銷 |
| 死鎖預防 | 固定鎖順序、TryEnterCriticalSection 超時退避、避免持有鎖呼叫外部代碼 |
| 偽共享 | 不同執行緒頻繁讀寫同一快取行的不同變數 → 显式對齊 alignas(64) 或分離熱點資料 |
| 除錯技巧 | Visual Studio: **執行緒視窗、平行堆疊、資料斷點、命名執行緒** (SetThreadDescription Win10+) |
四、選型建議
- 純算力/IO、無 UI、追求極致效能/可移植 → **Win32 API + 標準 C++11/17
<thread> <mutex> <condition_variable>**
- MFC 專案、需與 UI 互動、快速開發 → **MFC
AfxBeginThread + CSingleLock/CMultiLock**
- 新專案、跨平台、現代 C++ → **
std::thread、std::jthread (C++20)、std::counting_semaphore、std::latch/barrier**
結語
多執行緒是把雙面刃:用得好是效能倍增器,用不好是 Bug 製造機。**核心原則只有三條**:(1) **最小化共享狀態** (傾向不可變資料、訊息傳遞);(2) **鎖的範圍最小、持有時間最短**;(3) **永遠在固定順序獲取多把鎖**。掌握 Win32 底層機制讓你心中有數,善用 MFC/STD 封裝讓你手中無碼、心中有碼。願你的執行緒永遠無競爭、無死鎖、無飢餓——只剩下優雅的並行。