本文記錄基於 Windows API 的多線程程式開發核心函數:CreateThread、CloseHandle、CreateMutex、WaitForSingleObject、ReleaseMutex,並通過四個漸進式 Demo 示範線程創建、參數傳遞、互斥量同步、生產者-消費者模型(售票系統)。
HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全屬性,通常 NULL
SIZE_T dwStackSize, // 棧大小,0 為預設
LPTHREAD_START_ROUTINE lpStartAddress, // 線程函數入口
LPVOID lpParameter, // 傳給線程函數的參數
DWORD dwCreationFlags, // 0=立即運行 | CREATE_SUSPENDED=掛起
LPDWORD lpThreadId // 輸出線程 ID,可 NULL
);
DWORD WINAPI ThreadProc(LPVOID lpParameter); // 返回值作為線程退出碼
BOOL WINAPI CloseHandle(HANDLE hObject); // 釋放內核對象引用計數
DWORD WINAPI ThreadProc(LPVOID) {
for (int i = 0; i < 5; ++i) {
cout << "子線程: i = " << i << endl;
Sleep(100);
}
return 0;
}
int main() {
HANDLE h = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
CloseHandle(h); // 主線程不等待子線程,僅釋放句柄引用
for (int i = 0; i < 5; ++i) {
cout << "主線程: i = " << i << endl;
Sleep(100);
}
return 0;
}
輸出交錯:主/子線程並發執行,輸出順序不定。
struct ThreadData {
int maxCount;
char name[40];
};
DWORD WINAPI ThreadProc(LPVOID lp) {
auto* p = static_cast(lp);
for (int i = 0; i < p->maxCount; ++i) {
cout << p->name << " --- " << i << endl;
Sleep(100);
}
return 0;
}
int main() {
ThreadData d1{5, "線程1"}, d2{10, "線程2"};
HANDLE h1 = CreateThread(NULL, 0, ThreadProc, &d1, 0, NULL);
HANDLE h2 = CreateThread(NULL, 0, ThreadProc, &d2, 0, NULL);
CloseHandle(h1); CloseHandle(h2);
// 主線程同樣輸出...
}
問題:多線程並發輸出 cout 導致單行內容被打斷(如 主線程 === 線程1 --- 0 0),需同步。
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES, BOOL bInitialOwner, LPCTSTR lpName);
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); // INFINITE = 無限等待
BOOL ReleaseMutex(HANDLE hMutex);
HANDLE g_hMutex = CreateMutex(NULL, FALSE, NULL);
DWORD WINAPI ThreadProc(LPVOID lp) {
auto* p = static_cast(lp);
for (int i = 0; i < p->maxCount; ++i) {
WaitForSingleObject(g_hMutex, INFINITE);
cout << p->name << " --- " << i << endl;
Sleep(100);
ReleaseMutex(g_hMutex);
}
return 0;
}
// 主線程同樣用 Wait/Release 保護 cout
// 輸出整齊:每行完整屬於某一線程
場景:100 張票,8 個窗口並發出售,保證數據原子性與一致性。
struct Ticket { int count; char name[40]; };
struct ThdData { Ticket* pTicket; char winName[40]; };
DWORD WINAPI SaleTicket(LPVOID lp) {
auto* p = static_cast(lp);
while (true) {
WaitForSingleObject(g_hMutex, INFINITE);
if (p->pTicket->count > 0) {
int current = p->pTicket->count--;
cout << p->winName << " 出售第 " << current << " 張票";
if (current > 0) cout << " 成功! 剩餘 " << p->pTicket->count << " 張";
else cout << " 失敗! 已售罄";
cout << endl;
} else {
ReleaseMutex(g_hMutex);
break;
}
Sleep(10);
ReleaseMutex(g_hMutex);
}
return 0;
}
g_hMutex = CreateMutex(NULL, FALSE, NULL);
Ticket ticket{100, "北京->贛州"};
const int N = 8;
ThdData data[N];
HANDLE hThreads[N];
for (int i = 0; i < N; ++i) {
data[i].pTicket = &ticket;
sprintf(data[i].winName, "窗口%d", i);
WaitForSingleObject(g_hMutex, INFINITE);
cout << data[i].winName << " 開始出售 " << ticket.name << " 的票..." << endl;
ReleaseMutex(g_hMutex);
hThreads[i] = CreateThread(NULL, 0, SaleTicket, &data[i], 0, NULL);
CloseHandle(hThreads[i]); // 句柄可提前關閉,線程繼續運行
}
system("pause");
窗口0 開始出售 北京->贛州 的票... 窗口0 出售第 100 張票 成功! 剩餘 99 張 窗口1 開始出售 北京->贛州 的票... 窗口1 出售第 99 張票 成功! 剩餘 98 張 窗口0 出售第 98 張票 成功! 剩餘 97 張 ...
CreateThread 返回的句柄需 CloseHandle,否則洩漏內核對象;關閉句柄不終止線程。system("pause") 阻塞主線程)。count-- + 輸出),避免在鎖內做耗時操作(Sleep 放在鎖外更佳)。GetLastError() 診斷。std::thread + std::mutex + std::lock_guard 跨平台、RAII 安全、語法更簡潔,新專案優先考慮。