CreateThread 是 Windows API 建立執行緒的基礎函式。雖然 Microsoft 建議使用 _beginthreadex(正確初始化 CRT)或 C++11 std::thread,但在維護舊專案、特定平台限制或需精確控制執行緒屬性(堆疊大小、安全性描述元、CREATE_SUSPENDED 等)時,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=不接收
);
struct ThreadParam {
int taskId;
uint16_t channelId;
uint16_t sessionId;
// 可擴展:std::string, 智慧指標(需注意跨執行緒所有權), 同步物件控制代碼等
};
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, // 執行緒函式
¶m, // 傳遞結構體位址
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;
}
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 | 高併發、自動管理、可擴展 | 程式模型較複雜、非即時啟動 | 高效能伺服器、大量短任務 |
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),即可穩健交付。