`_CAsThread` 是整個線程控制體系的基石,負責封裝平台相關的線程操作(Windows `_beginthreadex`/`WaitForSingleObject`/`CloseHandle`),提供統一的啟動/停止/命名/異常處理介面,供上一篇宏生成的繼承類調用。
class _CAsThread
{
protected:
bool m_bExit; // 線程退出標誌
bool m_bStart; // 線程運行標誌
HANDLE m_hThread; // 線程句柄
char m_strName[100]; // 線程名稱(調試用)
void (*m_pExceptFun)(); // 結構化異常轉換函數指針
public:
_CAsThread(); // 構造函數初始化
virtual ~_CAsThread(); // 析構確保線程停止
virtual void WorkThread() = 0; // 純虛函數,由繼承類實現具體任務
// 核心控制介面
bool Start();
void PrepareStop();
bool Stop();
void SetName(const char* vName);
void SetTranslator(void (*vFun)()); // 設置 SEH 異常轉換器
};
_CAsThread::_CAsThread()
{
m_bExit = false;
m_bStart = false;
m_hThread = 0;
memcpy(m_strName, "AsThread", strlen("AsThread")+1);
m_pExceptFun = NULL;
}
_CAsThread::~_CAsThread()
{
Stop(); // 析構時自動停止線程,防止資源洩漏
}
// MSVC 專用:通過拋出特定異常設置線程名(僅調試器可見)
#define MS_VC_EXCEPTION 0x406d1388
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // 必須為 0x1000
LPCSTR szName; // 線程名字串
DWORD dwThreadID; // 線程 ID(-1 表示當前線程)
DWORD dwFlags; // 保留,必為 0
} THREADNAME_INFO;
void SetThreadName(DWORD dwThreadID, LPCTSTR szThreadName)
{
THREADNAME_INFO info = { 0x1000, szThreadName, dwThreadID, 0 };
__try
{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info);
}
__except(EXCEPTION_CONTINUE_EXECUTION) { /* 忽略異常 */ }
}
實用價值: 在 Visual Studio「線程」調試窗口中,線程將顯示為「AsThread」或自定義名稱,而非無意義的十六進制 ID,極大提升多線程調試效率。
bool _CAsThread::Start()
{
if (true == m_bStart) return true; // 已運行,冪等
if (0 == m_hThread)
{
unsigned threadID;
// _beginthreadex 是 Windows 推薦的線程創建 API(比 CreateThread 正確初始化 CRT)
m_hThread = (HANDLE)_beginthreadex(NULL, 0, &WorkItem, (void*)this, 0, &threadID);
int tHandle = (int)m_hThread;
if (0 == tHandle || -1 == tHandle) return false; // 創建失敗
SetThreadName(threadID, m_strName); // 設置調試器可見名稱
}
// 輸出調試啟動信息
char tDebugBuf[250] = {0};
#ifndef __BORLANDC__
sprintf_s(tDebugBuf, "------線程%s開始運行------\n", m_strName);
#else
sprintf(tDebugBuf, "------線程%s開始運行------\n", m_strName);
#endif
OutputDebugString(tDebugBuf); // 輸出到 VS「輸出」窗口 / DebugView
m_bExit = false;
m_bStart = true;
return true;
}
bool _CAsThread::Stop()
{
// 1. 設置退出標誌(通知工作循環退出)
m_bStart = false;
m_bExit = true;
// 2. 等待線程實際結束
int tHandle = (int)m_hThread;
if (0 != tHandle && -1 != tHandle)
{
WaitForSingleObject(m_hThread, INFINITE); // 無限等待
CloseHandle(m_hThread); // 釋放句柄
// 輸出調試關閉信息
char tDebugBuf[250] = {0};
#ifndef __BORLANDC__
sprintf_s(tDebugBuf, "------線程%s句柄關閉------\n", m_strName);
#else
sprintf(tDebugBuf, "------線程%s句柄關閉------\n", m_strName);
#endif
OutputDebugString(tDebugBuf);
}
m_hThread = 0;
return true;
}
unsigned __stdcall _CAsThread::WorkItem(void* vContext)
{
_CAsThread* pTimer = (_CAsThread*)vContext;
// 設置結構化異常轉換器(將 SEH 異常轉為 C++ 異常)
pTimer->SetTranslator();
// 工作循環
while (1)
{
if (true == pTimer->m_bExit) break; // 檢查退出標誌
pTimer->WorkThread(); // 調用繼承類實現的具體任務
}
// 輸出退出調試信息
char tDebugBuf[250] = {0};
#ifndef __BORLANDC__
sprintf_s(tDebugBuf, "------線程%s開始退出------\n", pTimer->m_strName);
#else
sprintf(tDebugBuf, "------線程%s開始退出------\n", pTimer->m_strName);
#endif
OutputDebugString(tDebugBuf);
return 0;
}
void _CAsThread::SetTranslator()
{
if (NULL == m_pExceptFun) return;
// 禁用系統錯誤彈窗,改為程序捕獲
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
// 設置轉換函數:當硬體異常(訪問違例、除零等)發生時,調用 m_pExceptFun
_set_se_translator(m_pExceptFun);
}
應用場景: 防止線程內硬體異常(如空指針訪問)導致整個進程崩潰無日誌,轉為 C++ 異常後可在 `WorkThread()` 中 `try-catch` 捕獲並記錄日誌、優雅退出。
classDiagram
class _CAsThread {
-m_bExit: bool
-m_bStart: bool
-m_hThread: HANDLE
-m_strName: char[100]
-m_pExceptFun: SETranslator
+Start() bool
+Stop() bool
+PrepareStop()
+SetName()
+SetTranslator()
#WorkThread() void* // 純虛函數
+WorkItem() unsigned // 靜態入口
}
class CTestThreadTest {
-m_Owner: CTestThread*
+WorkThread() void
}
_CAsThread <|-- CTestThreadTest : 繼承
CTestThreadTest --> CTestThread : m_Owner 指向
CTestThread : +Test() void
三篇文章構建了完整的「宏驅動成員函數綁定 + 基類封裝平台差異」線程框架,展示了 C++98/03 時代的經典工程智慧。但在 **C++11 及以上** 專案中,請優先選用標準庫:
std::thread + Lambda / std::bind:類型安全、無宏魔法、異常傳播標準std::jthread (C++20):自動 `join`、支援 `stop_token` 協作式取消std::async`/`std::future`:任務並行、返回值獲取更直觀std::mutex`/`std::condition_variable`/`std::atomic`:同步原語完備理解經典實現原理,是為了更好地駕馭現代工具。願這系列文章能幫你在多線程編程的道路上走得更穩、更遠!