pthread_create 的線程入口函數簽名固定為:
void* (*start_routine)(void*);
C++ 非靜態成員函數隱含 this 指標,實際簽名為 void* (MyClass::*)(void*),與要求不符且無法直接轉型。
class ThreadWorker {
public:
// 啟動線程
bool start() {
return pthread_create(&threadId_, nullptr, &ThreadWorker::threadEntry, this) == 0;
}
// 等待線程結束
void join() {
pthread_join(threadId_, nullptr);
}
protected:
// 純虛函數:子類實作具體邏輯
virtual void run() = 0;
private:
pthread_t threadId_;
// 靜態成員函數:符合 pthread 簽名
static void* threadEntry(void* arg) {
ThreadWorker* self = static_cast<ThreadWorker*>(arg);
self->run(); // 呼叫虛函數,實現多態
return nullptr;
}
};
// 使用範例
class MyTask : public ThreadWorker {
protected:
void run() override {
// 具體線程邏輯
printf("Thread running in MyTask\n");
}
};
this 指標,簽名與 void*(*)(void*) 完全匹配。this:pthread_create 最後一個參數 arg 傳入 this。static_cast 將 void* 還原為類別指標。run() 觸發動態綁定,執行子類邏輯。class Thread {
public:
template <typename Callable, typename... Args>
explicit Thread(Callable&& f, Args&&... args) {
auto task = std::make_shared<std::packaged_task<void()>>(
std::bind(std::forward<Callable>(f), std::forward<Args>(args)...)
);
pthread_create(&tid_, nullptr, &Thread::entry, task.get());
task_ = std::move(task); // 延長生命週期
}
~Thread() { join(); }
void join() { if (tid_) pthread_join(tid_, nullptr); }
private:
pthread_t tid_ = 0;
std::shared_ptr<void> task_; // 管理任務生命週期
static void* entry(void* arg) {
auto task = static_cast<std::packaged_task<void()>*>(arg);
(*task)();
return nullptr;
}
};
| 錯誤做法 | 後果 | 正確做法 |
|---|---|---|
| 強制轉型非靜態成員函數 | 未定義行為,崩潰 | 使用靜態成員函數或自由函數 |
| 傳遞臨時物件指標 | 線程執行時物件已銷毀 | 確保物件生命週期 ≥ 線程生命週期 |
| 忽略返回值檢查 | 線程建立失敗無感知 | 檢查 pthread_create 返回碼 |
| 主線程提前結束 | 進程終止,所有線程被強制殺死 | 呼叫 pthread_join 或 pthread_detach |
C++11 起推薦使用 std::thread,原生支援成員函數、Lambda、可呼叫物件,無需手動轉型:
class MyClass {
void memberFunc(int x) { /* ... */ }
};
MyClass obj;
std::thread t(&MyClass::memberFunc, &obj, 42); // 直接綁定成員函數
t.join();
靜態成員函數橋接模式是 C++98/03 時代使用 pthread 的標準作法,核心在於型別擦除與狀態回傳。現代專案應優先採用 std::thread,若維護舊代碼則需嚴格遵守生命週期管理與錯誤檢查。