首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC 多線程程式設計全攻略:從 Win32 API 到 CWinThread 封裝

     編輯:關於VC++

多線程是現代 Windows 應用程式提升回應性、並行處理的關鍵技術。MFC 在 Win32 線程模型之上,提供了 CWinThread 類別與 AfxBeginThread 等全域函數,封裝了線程建立、同步、訊息迴路、例外處理等複雜細節。本文系統梳理 Win32 線程基礎、同步物件、MFC 線程類別體系與實戰開發模式。

一、 Win32 進程與線程基礎回顧

1. 核心概念

  • 進程:擁有獨立虛擬位址空間、代碼、資料、核心物件(檔案、管道、同步物件等)的可執行單元。
  • 線程:CPU 調度的基本單位,隸屬於某進程,共享進程位址空間、全域變數、核心物件。一個進程至少有一個主線程,可建立多個工作者線程。

線程優於進程的優勢:建立/切換速度快(無需重新映射映像)、共享記憶體簡化通訊。

2. 進程操作 API

MFC 未封裝進程管理,直接使用 Win32 API:

  • CreateProcess:建立新進程,參數涵蓋應用程式名、命令列、安全屬性、控制代碼繼承、建立旗標、環境塊、啟動資訊等。成功返回 PROCESS_INFORMATION(含進程/主線程句柄與 ID)。
  • 進程終止方式:ExitProcess、主線程返回、最後一個線程結束、TerminateProcess(強制,不執行 DLL 入口點)。

3. 線程建立與管理

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    DWORD dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags, // CREATE_SUSPENDED 掛起建立
    LPDWORD lpThreadId
);
  • 優先級:進程優先級類別 + 線程優先級層級,透過 SetPriorityClass/SetThreadPriority 調整。
  • 終止方式:ExitThread、線程函數返回、TerminateThread、進程終止。注意:TerminateThread/Process 會跳過 DllMain,導致資源洩漏。

4. 線程局部儲存 (TLS)

Win32 TLS 機制:TlsAlloc 取得索引 → TlsSetValue 存指標 → TlsGetValue 取指標 → TlsFree 釋放。DLL 中典型用法:DllMain 中 DLL_PROCESS_ATTACH 分配索引、DLL_THREAD_ATTACH 為新線程配置記憶體並 TlsSetValue、DLL_THREAD_DETACH 釋放記憶體。

5. 同步物件與等待函數

物件適用範圍關鍵 API
Critical Section同進程線程間InitializeCriticalSection, Enter/LeaveCriticalSection
Event跨進程CreateEvent, Set/Reset/PulseEvent(自動/手動重置)
Mutex跨進程CreateMutex, WaitForSingleObject, ReleaseMutex
Semaphore跨進程,資源計數CreateSemaphore, Wait/ReleaseSemaphore

等待函數族群:WaitForSingleObject、WaitForMultipleObjects、MsgWaitForMultipleObjectsEx(支援訊息泵與 APC)、SignalObjectAndWait(原子性釋放並等待)。

二、 MFC 線程封裝:CWinThread 與 AfxBeginThread

1. 兩種線程模型

  • 使用者介面線程 (UI Thread):擁有訊息泵,處理視窗訊息。CWinApp 即為典型 UI 線程。需從 CWinThread 衍生,覆寫 InitInstance(必須)、ExitInstance,使用 DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE 支援動態建立。
  • 工作者線程 (Worker Thread):無訊息泵,執行背景運算。無需衍生類別,僅提供控制函數 UINT AFX_CDECL ThreadProc(LPVOID pParam)。

2. AfxBeginThread 兩個重載版本

// UI 線程
CWinThread* AfxBeginThread(
    CRuntimeClass* pThreadClass, int nPriority, UINT nStackSize,
    DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs);

// Worker 線程
CWinThread* AfxBeginThread(
    AFX_THREADPROC pfnThreadProc, LPVOID pParam,
    int nPriority, UINT nStackSize, DWORD dwCreateFlags,
    LPSECURITY_ATTRIBUTES lpSecurityAttrs);

統一流程:建立 MFC 線程物件 → 呼叫 CWinThread::CreateThread → 內部呼叫 _beginthreadex 指定入口函數 _AfxThreadEntry → 同步事件 hEvent/hEvent2 協調父/子線程啟動順序(圖 8-1、8-2)。

3. 線程啟動結構 _AFX_THREAD_STARTUP

作為 _beginthreadex 參數傳遞,包含父線程狀態指標、新線程物件指標、建立旗標、新處理器、同步事件、錯誤碼。子線程在 _AfxThreadEntry 中初始化自身線程狀態、繼承父線程模組狀態,隨後進入控制函數或 Run() 訊息迴路。

4. 線程結束與清理

  • AfxEndThread:清理 MFC 物件、釋放 TLS 記憶體、呼叫 CWinThread::Delete(預設 delete this 並 CloseHandle)、呼叫 _endthreadex 釋放 CRT 資源(不關閉線程句柄)。
  • m_bAutoDelete = TRUE(預設)自動銷毀 CWinThread 物件;若需線程結束後保留物件檢查結果,設為 FALSE 並手動刪除。

三、 線程訊息迴路與背景處理

1. CWinThread::Run 預設實現

核心邏輯:PumpMessage() 取得/轉譯/分派訊息 → 無訊息時呼叫 OnIdle(lIdleCount) 執行閒置處理(工具列更新等) → 收到 WM_QUIT 返回退出碼並呼叫 ExitInstance。

2. 自訂訊息迴路:長時間運算仍保持回應

while (bDoingBackgroundProcessing) {
    MSG msg;
    while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
        if (!PumpMessage()) { bDoingBackgroundProcessing = FALSE; ::PostQuitMessage(0); break; }
    }
    LONG lIdle = 0;
    while (AfxGetApp()->OnIdle(lIdle++)); // 執行 MFC 閒置處理
    // 執行背景運算片段
}

同理,等待同步物件時可用 WaitForSingleObject(hEvent, 0) == WAIT_TIMEOUT 作為迴圈條件。

四、 MFC 狀態管理預告

MFC 引入線程狀態、進程狀態、模組狀態等概念,支撐跨模組資源存取、句柄映射隔離等核心功能。詳細機制將於下一章(第 9 章)深入剖析。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved