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

Windows API 多線程開發實戰:CreateThread、互斥量同步與售票系統模擬

     編輯:關於C++

Windows API 多線程開發實戰

本文記錄基於 Windows API 的多線程程式開發核心函數:CreateThread、CloseHandle、CreateMutex、WaitForSingleObject、ReleaseMutex,並通過四個漸進式 Demo 示範線程創建、參數傳遞、互斥量同步、生產者-消費者模型(售票系統)。

一、線程創建與關閉

CreateThread 原型

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); // 釋放內核對象引用計數

二、Demo 1:最簡單的線程

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;
}

輸出交錯:主/子線程並發執行,輸出順序不定。

三、Demo 2:傳遞參數給線程

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),需同步。

四、互斥量同步機制

核心 API

HANDLE CreateMutex(LPSECURITY_ATTRIBUTES, BOOL bInitialOwner, LPCTSTR lpName);
DWORD  WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); // INFINITE = 無限等待
BOOL   ReleaseMutex(HANDLE hMutex);

Demo 3:同步輸出

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
// 輸出整齊:每行完整屬於某一線程

五、Demo 4:多窗口售票系統模擬

場景: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 張
...

六、關鍵經驗總結

  1. 句柄管理:CreateThread 返回的句柄需 CloseHandle,否則洩漏內核對象;關閉句柄不終止線程。
  2. 參數生命週期:傳遞棧上結構體指針時,必須確保主線程在所有子線程結束前不銷毀該結構體(Demo 中用 system("pause") 阻塞主線程)。
  3. 互斥量粒度:保護共享數據的最小臨界區(如 count-- + 輸出),避免在鎖內做耗時操作(Sleep 放在鎖外更佳)。
  4. 錯誤檢查:所有 API 返回值均應檢查,失敗時調用 GetLastError() 診斷。
  5. 現代替代:C++11 std::thread + std::mutex + std::lock_guard 跨平台、RAII 安全、語法更簡潔,新專案優先考慮。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved