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

C++ CreateThread 參數傳遞與執行緒函式完整實作指南

     編輯:C++ 入門

前言

CreateThread 是 Windows API 建立執行緒的基礎函式。雖然 Microsoft 建議使用 _beginthreadex(正確初始化 CRT)或 C++11 std::thread,但在維護舊專案、特定平台限制或需精確控制執行緒屬性(堆疊大小、安全性描述元、CREATE_SUSPENDED 等)時,CreateThread 仍是必備技能。本文詳細解析參數結構傳遞、型別轉換、生命週期管理與常見陷阱。

一、CreateThread 完整原型

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES   lpThreadAttributes,  // 安全性描述元,NULL=預設
    SIZE_T                  dwStackSize,         // 堆疊大小,0=預設 1MB
    LPTHREAD_START_ROUTINE  lpStartAddress,      // 執行緒函式位址
    LPVOID                  lpParameter,         // 傳遞給執行緒函式的單一參數
    DWORD                   dwCreationFlags,     // 0=立即執行, CREATE_SUSPENDED=暫停
    LPDWORD                 lpThreadId           // 接收執行緒 ID,NULL=不接收
);

二、參數結構體設計與傳遞模式

1. 定義參數結構(值型別,避免指標失效)

struct ThreadParam {
    int         taskId;
    uint16_t    channelId;
    uint16_t    sessionId;
    // 可擴展:std::string, 智慧指標(需注意跨執行緒所有權), 同步物件控制代碼等
};

2. 主執行緒建立與啟動

DWORD WINAPI WorkerThread(LPVOID lpParam);  // 前置宣告

int main() {
    ThreadParam param{1, 100, 200};          // 堆疊配置,生命週期覆蓋執行緒啟動
    // 若需動態配置或共享,改用 heap + 共享所有權:
    // auto pParam = std::make_shared(ThreadParam{1,100,200});

    DWORD threadId = 0;
    HANDLE hThread = CreateThread(
        nullptr,               // 預設安全性
        0,                     // 預設堆疊
        WorkerThread,          // 執行緒函式
        &param,                // 傳遞結構體位址
        0,                     // 立即執行
        &threadId              // 接收 ID
    );

    if (hThread == nullptr) {
        DWORD err = GetLastError();
        std::cerr << "CreateThread 失敗: " << err << std::endl;
        return 1;
    }

    // 視需求決定同步方式:
    // WaitForSingleObject(hThread, INFINITE); // 等待結束
    // CloseHandle(hThread);                   // 釋放核心物件參考
    // 若主執行緒提前結束,需確保 param 仍有效(如改用 shared_ptr)

    std::cout << "執行緒已啟動, ID=" << threadId << std::endl;
    // 此處模擬主程式繼續執行...
    Sleep(1000);
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    return 0;
}

3. 執行緒函式實作與型別安全轉換

DWORD WINAPI WorkerThread(LPVOID lpParam) {
    // 1. 型別安全轉換
    ThreadParam* p = static_cast<ThreadParam*>(lpParam);
    if (!p) return 1; // 防禦性檢查

    // 2. 取得參數(複製到區域變數,避免後續主執行緒修改/釋放)
    const int taskId = p->taskId;
    const uint16_t chId = p->channelId;
    const uint16_t sessId = p->sessionId;

    // 3. 實際業務邏輯
    try {
        RunBusinessLogic(taskId, chId, sessId); // 自訂函式
    }
    catch (const std::exception& ex) {
        LogError("WorkerThread 例外: %s", ex.what());
    }
    catch (...) {
        LogError("WorkerThread 未知例外");
    }

    return 0; // 執行緒結束代碼
}

三、關鍵陷阱與最佳實踐

陷阱後果解決方案
傳遞區域結構體位址,主執行緒函式返回導致結構體銷毀執行緒存取懸空指標 → 未定義行為/崩潰延長結構體生命週期(static、heap+shared_ptr、等待執行緒結束再銷毀)
直接傳遞類別物件 this 指標,物件在執行緒運行中被刪除相同,存取已釋放記憶體使用 shared_ptr 延長物件壽命,或確保物件存活至執行緒結束
執行緒函式拋出 C++ 例外未捕獲例外跨越 Win32 邊界 → 進程終止執行緒函式最外層包裹 try-catch(...),記錄並轉為錯誤碼返回
使用 CreateThread 而非 _beginthreadex,CRT 未初始化strtok, errno, locale 等 CRT 功能失效/資料競爭優先用 _beginthreadex 或 std::thread;必須用 CreateThread 時,手動呼叫 _initterm 初始化 CRT(不建議)
未關閉執行緒控制代碼核心物件洩漏,長期運行耗盡控制代碼表確實呼叫 CloseHandle(hThread);搭配 RAII 包裝(unique_ptr with custom deleter)

四、現代化替代方案對比

| 方案 | 優點 | 缺點 | 適用場景 | |------|------|------|----------| | std::thread (C++11) | 型別安全、RAII、可移植、支援 lambda/仿函數 | 需 C++11 編譯器、無法精確控制 Win32 專屬屬性 | 新專案、跨平台、一般用途 | | _beginthreadex | 正確初始化 CRT、支援 Win32 旗標 | 仍需手動 CloseHandle、非標準 C++ | 維護舊 VC++ 專案、需 CRT 功能 | | CreateThread | 完整 Win32 控制、零 CRT 依賴 | CRT 未初始化、手動資源管理、易出錯 | 驅動程式、殼層程式碼、極度精簡環境 | | 執行緒池 ThreadPool / IOCP | 高併發、自動管理、可擴展 | 程式模型較複雜、非即時啟動 | 高效能伺服器、大量短任務 |

五、RAII 包裝範例(建議生產環境使用)

class ScopedThreadHandle {
    HANDLE h_;
public:
    explicit ScopedThreadHandle(HANDLE h = nullptr) : h_(h) {}
    ~ScopedThreadHandle() { if (h_) CloseHandle(h_); }
    ScopedThreadHandle(const ScopedThreadHandle&) = delete;
    ScopedThreadHandle& operator=(const ScopedThreadHandle&) = delete;
    ScopedThreadHandle(ScopedThreadHandle&& other) noexcept : h_(other.h_) { other.h_ = nullptr; }
    ScopedThreadHandle& operator=(ScopedThreadHandle&& other) noexcept {
        if (h_) CloseHandle(h_);
        h_ = other.h_; other.h_ = nullptr; return *this;
    }
    HANDLE get() const { return h_; }
    DWORD wait(DWORD ms = INFINITE) const { return WaitForSingleObject(h_, ms); }
};

// 使用範例
ScopedThreadHandle hThread(CreateThread(...));
if (!hThread.get()) { /* error */ }
hThread.wait(); // 自動關閉

結語

CreateThread 是 Windows 多執行緒的基石,掌握「參數結構體設計 → 生命週期管理 → 型別安全轉換 → 例外隔離 → 控制代碼 RAII」完整鏈路,能避開 90% 的多執行緒坑。新專案請優先採用 std::thread 或執行緒池;維護舊碼時,嚴守上述規範並配合靜態分析工具(/analyze, Clang-Tidy),即可穩健交付。

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