在《C++ 線程控制實戰(一)》中,我們設計了線程控制基類 `_CAsThread` 的介面,定義了 `Start()`、`Stop()`、`SetName()` 等核心方法。但有一個關鍵痛點未解決:如何讓線程直接執行類的成員函數? 本期將通過「宏 + 繼承」技巧,優雅地實現線程與任意類成員函數的綁定。
利用宏在編譯期為每個「宿主類 + 成員函數」組合生成一個繼承自 `_CAsThread` 的專屬類,並在其 `WorkThread()` 中回調宿主對象的目標成員函數。
// 宏定義:生成綁定宿主類成員函數的線程類
#define CAsThread(classname, vFunc) \
class classname##vFunc : public _CAsThread \
{ \
protected: \
void WorkThread(void) \
{ \
if (0 != m_Owner) \
m_Owner->vFunc(); \
} \
public: \
classname* m_Owner; \
}; \
classname##vFunc
以 `CAsThread(CTestThread, Test)` 為例,預處理後展開為:
class CTestThreadTest : public _CAsThread
{
protected:
void WorkThread(void)
{
if (0 != m_Owner)
m_Owner->Test(); // 調用宿主對象的 Test() 成員函數
}
public:
CTestThread* m_Owner;
};
CTestThreadTest // 返回生成的類型名,用於聲明成員變量
class CTestThread
{
public:
// 線程要執行的成員函數
void Test()
{
printf("Hello TestThread\n");
Sleep(1000);
}
public:
// 使用宏聲明線程成員對象
// 展開後相當於:CTestThreadTest m_Thread;
CAsThread(CTestThread, Test) m_Thread;
};
// 使用方式
CTestThread testObj;
testObj.m_Thread.m_Owner = &testObj; // 關鍵:綁定宿主對象指針
testObj.m_Thread.Start(); // 啟動線程,將執行 Test()
class CWorker
{
public:
void TaskDownload() { /* 下載邏輯 */ }
void TaskProcess() { /* 處理邏輯 */ }
void TaskUpload() { /* 上傳邏輯 */ }
public:
// 一個類綁定三個不同線程任務
CAsThread(CWorker, TaskDownload) m_DownloadThread;
CAsThread(CWorker, TaskProcess) m_ProcessThread;
CAsThread(CWorker, TaskUpload) m_UploadThread;
};
// 初始化綁定
CWorker worker;
worker.m_DownloadThread.m_Owner = &worker;
worker.m_ProcessThread.m_Owner = &worker;
worker.m_UploadThread.m_Owner = &worker;
// 並發啟動
worker.m_DownloadThread.Start();
worker.m_ProcessThread.Start();
worker.m_UploadThread.Start();
| 優點 | 缺點/注意事項 |
|---|---|
| 語法簡潔,調用直觀 | 宏調試困難,編譯錯誤訊息難讀 |
| 零運行時開銷(純編譯期展開) | 每個綁定組合生成一個類,代碼膨脹風險 |
| 完美解決成員函數綁定難題 | 需手動設置 `m_Owner`,易忘導致空指針崩潰 |
| 支援同類多線程不同任務 | 現代 C++ (C++11+) 推薦用 `std::thread` + `std::bind`/Lambda |
#include
#include
class ModernWorker
{
public:
void TaskDownload() { /* ... */ }
void TaskProcess() { /* ... */ }
void StartTasks()
{
// Lambda 捕獲 this,極其簡潔安全
std::thread t1([this] { this->TaskDownload(); });
std::thread t2([this] { this->TaskProcess(); });
t1.detach(); // 或 join()
t2.detach();
}
};
最佳實踐建議: 維護舊專案(C++98/03)時,本文宏方案是經典有效選擇;新專案強烈推薦使用 `std::thread` + Lambda,類型安全、異常處理完善、標準化程度高。
下一篇將深入實現 `_CAsThread` 基類核心邏輯:線程啟動/停止狀態機、異常轉換器、線程命名(用於調試)、跨平台考量等硬核細節。敬請期待!