每個 std::thread 物件皆處於兩種狀態之一:可連接 或 不可連接。可連接的 std::thread 對應一個真實存在的底層執行緒——該執行緒可能正在運行、被阻塞、或等待排程。不可連接的 std::thread 則無對應的底層執行緒,主要包含以下四種情況:
std::thread 的可連接性至關重要,原因在於:若可連接的執行緒物件被解構,程式會被終止。這是標準委員會為避免隱式 join 或 detach 導致更嚴重問題(效能異常、未定義行為)而做出的設計決策。
假設有一個函式 doWork,需在背景執行緒執行耗時的過濾運算,主執行緒則檢查條件決定是否等待結果:
constexpr auto tenMillion = 10'000'000; // C++14 數字分隔符
bool doWork(std::function<bool(int)> filter, int maxVal = tenMillion) {
std::vector<int> goodVals;
std::thread t([&filter, maxVal, &goodVals] {
for (auto i = 0; i <= maxVal; ++i) {
if (filter(i)) goodVals.push_back(i);
}
});
auto nh = t.native_handle(); // 取得原生句柄以設定優先權
// ... 使用 nh 設定執行緒優先權 ...
if (conditionsAreSatisfied()) {
t.join();
performComputation(goodVals);
return true;
}
return false; // 危險!t 仍可連接,析構時會 terminate
}
若 conditionsAreSatisfied() 回傳 false 或拋出異常,t 在離開作用域時仍為可連接狀態,導致程式終止。
goodVals)在函式返回時銷毀,但脫離的執行緒仍持有其引用,導致堆疊記憶體被破壞,除錯極其困難。在所有路徑上確保 std::thread 變為不可連接,最可靠的方式是使用 RAII(資源獲取即初始化)模式。標準庫未提供 std::thread 的 RAII 包裝器,因為委員會不想預設選擇 join 或 detach。但我們可自行實作:
class ThreadRAII {
public:
enum class DtorAction { join, detach };
ThreadRAII(std::thread&& t, DtorAction a)
: action(a), t(std::move(t)) {}
~ThreadRAII() {
if (t.joinable()) {
if (action == DtorAction::join) {
t.join();
} else {
t.detach();
}
}
}
std::thread& get() { return t; }
// 支援移動語意
ThreadRAII(ThreadRAII&&) = default;
ThreadRAII& operator=(ThreadRAII&&) = default;
private:
DtorAction action;
std::thread t;
};
關鍵設計細節:
native_handle() 等介面。bool doWork(std::function<bool(int)> filter, int maxVal = tenMillion) {
std::vector<int> goodVals;
ThreadRAII t(
std::thread([&filter, maxVal, &goodVals] {
for (auto i = 0; i <= maxVal; ++i) {
if (filter(i)) goodVals.push_back(i);
}
}),
ThreadRAII::DtorAction::join // 析構時 join
);
auto nh = t.get().native_handle();
// ... 設定優先權 ...
if (conditionsAreSatisfied()) {
t.get().join(); // 明確 join,RAII 析構時 t 已不可連接
performComputation(goodVals);
return true;
}
return false; // 安全:RAII 析構會 join
}
此處選擇 join 而非 detach,因為 detach 會導致區域變數生命週期問題。雖然 join 可能造成效能異常(條款 39 將討論如何避免),但比起未定義行為或程式終止,效能異常是三害中最輕微者。