現代 Windows 同時運行多進程,單進程內又可劃分多線程(獨立執行流)。多線程提供多任務處理能力,是當代軟件開發的基石。單線程軟件已不可想象。本文針對線程間通信、同步等高頻問題展開探討。
CWinThread 對象。單進程至少有一主線程(main/WinMain 入口),主線程結束 → 進程結束。可按需創建多線程並行運行。| 類型 | 用途 | 創建方式 |
|---|---|---|
| 用戶界面線程 | 處理用戶輸入、響應消息/事件 | 從 CWinThread 派生,用 DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE,調用 AfxBeginThread 重載版本啟動 |
| 工作線程 | 後台計算、調度、串口讀寫等 | 無需派生類,編寫控制函數,直接調用 AfxBeginThread(pfnThreadProc, param, priority) |
注意: 所有線程共享父進程變量,但每個線程可擁有自己的局部變量(棧上)。
CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0, // CREATE_SUSPENDED 掛起啟動
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
UINT ThreadFunc(LPVOID pParam) { /* 工作邏輯 */ return 0; }
CWinThread* pThread = AfxBeginThread(ThreadFunc, param, priority);
int GetThreadPriority();
BOOL SetThreadPriority(int nPriority); // 相對於所在優先權層次
// 優先權層次需用 Win32 API: GetPriorityClass/SetPriorityClass
設置優先級需線程創建時具備 THREAD_SET_INFORMATION 權限。
SuspendThread(); // 暫停
ResumeThread(); // 恢復
// 多次 Suspend 需同次數 Resume 才能恢復
AfxEndThread() 自殺。TerminateThread(hThread, dwExitCode) + CloseHandle() 釋放棧(不推薦,易導致資源洩漏)。BOOL bEnd = FALSE; // 全局標誌
UINT ThreadFunction(LPVOID pParam) {
while (!bEnd) { Beep(100,100); Sleep(1000); }
return 0;
}
void CTestView::OnInitialUpdate() {
hWnd = GetSafeHwnd();
pThread = AfxBeginThread(ThreadFunction, hWnd);
pThread->m_bAutoDelete = FALSE; // 手動刪除
}
void CTestView::OnDestroy() {
bEnd = TRUE; // 設置標誌
WaitForSingleObject(pThread->m_hThread, INFINITE); // 等待結束
delete pThread; // 釋放對象
CView::OnDestroy();
}
次級線程為主線程完成任務,需建立通信通道。主要方法:全局變量、事件對象、消息。
每線程擁有消息隊列(工作線程亦然)。
#define WM_USERMSG (WM_USER + 100)
// 發送
::PostMessage(hWnd, WM_USERMSG, 0, 0);
// 或
CWinThread::PostThreadMessage();
UINT ThreadFunction(LPVOID pParam) {
while (!bEnd) { Beep(100,100); Sleep(1000); }
::PostMessage(hWnd, WM_USERMSG, 0, 0); // 通知主窗口
return 0;
}
LONG CTestView::OnThreadEnded(WPARAM wParam, LPARAM lParam) {
AfxMessageBox(_T("Thread ended."));
return 0;
}
若工作線程也需接收消息,可在控制函數中用 ::GetMessage() 實現消息循環,隊列空時自動讓出 CPU。
用 CEvent 對象,兩態:有信號/無信號。線程等待有信號事件執行操作。
CEvent threadStart, threadEnd;
UINT ThreadFunction(LPVOID pParam) {
::WaitForSingleObject(threadStart.m_hObject, INFINITE); // 等待啟動信號
AfxMessageBox(_T("Thread start."));
while (!bEnd) {
Beep(100,100); Sleep(1000);
int result = ::WaitForSingleObject(threadEnd.m_hObject, 0); // 非阻塞檢查
if (result == WAIT_OBJECT_0) bEnd = TRUE;
}
::PostMessage(hWnd, WM_USERMSG, 0, 0);
return 0;
}
void CTestView::OnInitialUpdate() {
hWnd = GetSafeHwnd();
threadStart.SetEvent(); // 發送啟動信號
pThread = AfxBeginThread(ThreadFunction, hWnd);
pThread->m_bAutoDelete = FALSE;
}
void CTestView::OnDestroy() {
threadEnd.SetEvent(); // 發送結束信號
WaitForSingleObject(pThread->m_hThread, INFINITE);
delete pThread;
}
運行效果:關閉程序時才彈出「Thread ended」,證明線程正常同步結束。
多線程訪問共享變量需同步,防止數據競爭。MFC 提供 7 個同步類,分兩類:
CSyncObject、CSemaphore、CMutex、CCriticalSection、CEventCMultiLock、CSingleLock同一時刻僅允許一線程訪問受保護數據。共享 CCriticalSection 對象,訪問前 Lock(),結束 Unlock()。
#include "afxmt.h"
int array[10], destarray[10];
CCriticalSection Section;
UINT WriteThread(LPVOID) {
Section.Lock();
for (int x=0; x<10; x++) array[x] = x;
Section.Unlock();
return 0;
}
UINT ReadThread(LPVOID) {
Section.Lock();
for (int x=0; x<10; x++) destarray[x] = array[x];
Section.Unlock();
return 0;
}
結果:destarray 正確複製 0-9。無同步則數據亂序。
功能類似臨界區,但可跨進程同步。需配合 CSingleLock/CMultiLock 使用。
CMutex Section;
UINT WriteThread(LPVOID) {
CSingleLock lock(&Section);
lock.Lock();
for (int x=0; x<10; x++) array[x] = x;
lock.Unlock();
return 0;
}
// ReadThread 同理
允許多線程同時訪問(計數器機制)。創建 CSemaphore(初始計數, 最大計數),Lock() 遞減,Unlock() 遞增。
CSemaphore* semaphore = new CSemaphore(2, 2); // 允許2線程同時訪問
HWND hWnd = GetSafeHwnd();
AfxBeginThread(ThreadProc1, hWnd);
AfxBeginThread(ThreadProc2, hWnd);
AfxBeginThread(ThreadProc3, hWnd);
UINT ThreadProc1(LPVOID param) {
CSingleLock lock(semaphore);
lock.Lock();
Sleep(10000);
::MessageBox((HWND)param, _T("Thread1 had Access"), _T("Thread1"), MB_OK);
return 0;
}
// ThreadProc2/3 相同
效果:前兩線程立即彈窗,第三線程等待 10 秒後才獲取信號量彈窗。
多線程賦予應用高效、快速、安全的數據處理能力。掌握線程創建、優先級控制、優雅退出、消息/事件通信、臨界區/互斥/信號量同步,是構建健壯 Windows 應用的核心技能。