Windows API 和 MFC 均無直接獲取線程狀態 (運行/掛起/結束) 的函數。
但可以通過 **`SuspendThread` / `ResumeThread` 返回的掛起計數器** 間接判斷。
BOOL IsThreadSuspended(HANDLE hThread) {
// 1. 先檢查是否已結束
DWORD exitCode;
if (GetExitCodeThread(hThread, &exitCode) && exitCode != STILL_ACTIVE) {
return FALSE; // 已結束,非掛起
}
// 2. 嘗試恢復一次,觀察返回值
DWORD prevCount = ResumeThread(hThread);
if (prevCount == 0) {
// 原計數 0 → 原為運行中,ResumeThread 導致計數變為 0 (無效操作)
// 但為了不改變狀態,需立即掛起回去
SuspendThread(hThread); // 補償
return FALSE; // 原非掛起
}
else if (prevCount == 1) {
// 原計數 1 → 原為掛起,現已恢復運行
// 補償:重新掛起
SuspendThread(hThread);
return TRUE; // 原為掛起
}
else { // prevCount > 1
// 原計數 >1 → 原為深度掛起,現計數-1但仍掛起
// 無需補償,狀態未質變
return TRUE; // 原為掛起
}
}
class CThreadUtil {
public:
static BOOL IsThreadRunning(CWinThread* pThread) {
if (!pThread || !pThread->m_hThread) return FALSE;
DWORD exitCode;
if (GetExitCodeThread(pThread->m_hThread, &exitCode)) {
return exitCode == STILL_ACTIVE;
}
return FALSE;
}
static BOOL IsThreadSuspended(CWinThread* pThread) {
if (!IsThreadRunning(pThread)) return FALSE;
HANDLE hThread = pThread->m_hThread;
DWORD prev = ResumeThread(hThread);
if (prev == 0) { SuspendThread(hThread); return FALSE; }
if (prev == 1) { SuspendThread(hThread); return TRUE; }
return TRUE; // prev > 1
}
};
| 風險 | 說明 | 對策 |
|---|---|---|
| 競態條件 | 檢查瞬間線程狀態可能已變 | 僅作調試/診斷用,**業務邏輯嚴禁依賴此狀態** |
| 副作用 | ResumeThread/SuspendThread 改變真實計數 | 必須成對調用補償,**極短時間窗口**仍可能導致死鎖/饑餓 |
| 死鎖風險 | 線程持有鎖時被強制掛起 | **永遠不要**用 SuspendThread/TerminateThread 同步線程 |
| 正確同步 | 事件、互斥量、條件變量、原子操作 | 使用 `CEvent`、`CCriticalSection`、`std::condition_variable` |
// 線程主循環
while (true) {
DWORD wait = WaitForSingleObject(hEvent, INFINITE);
if (wait == WAIT_OBJECT_0) {
// 處理任務
ResetEvent(hEvent);
}
// 檢查退出標誌...
}
// 外部控制
SetEvent(hEvent); // 喚醒/觸發
// 狀態由事件信號量隱式管理,無需查詢線程狀態
原則:**不要問線程「你在幹什麼」,而要告訴線程「幹這個」**。