? ? ? ? std::thread是 C++11 標準庫中用于多線程編程的核心類,提供線程的創建、管理和同步功能。下面我們一一講解。
一.構造函數
官網的構造函數如下:
1.默認構造函數和線程創建
thread() noexcept;
作用:創建一個?std::thread
?對象,但不關聯任何實際線程?(即空線程對象)。
如:
std::thread t; // 空線程對象,不執行任何操作
注意:空線程對象不可調用?join()
?或?detach()
,需先綁定有效線程。
2.初始化構造函數
template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args);
- 作用:創建一個新線程,新線程會立即執行?
fn(args...)函數
。 - ?參數:
fn
:可調用對象(函數、Lambda、函數對象等)。args
:傳遞給?fn
?的參數
如:?
void print(int x) { std::cout << x; }
std::thread t(print, 42); // 線程執行 print(42)
t.join();
?這里要注意下面幾點
explicit
?禁止隱式類型轉換(如避免誤將函數指針轉線程對象)。- 參數默認按值拷貝傳遞,若需傳引用需用?
std::ref
- 示例:
int x = 42;
std::thread t([](int& v) { v++; }, std::ref(x)); // 傳遞引用
3.拷貝構造函數(被刪除)?
thread(const thread&) = delete;?
- ?作用:禁止拷貝構造線程對象(線程資源不可復制)。
- 示例:
-
std::thread t1([]{ /* ... */ }); std::thread t2 = t1; // 編譯錯誤!拷貝構造被禁用
- ?原因:線程是獨占資源,拷貝可能導致線程重復管理(如多次?
join()
)。
?4.移動構造函數
thread(thread&& x) noexcept;
- ?作用:將線程所有權從?
x
?轉移到新對象(x
?變為空線程對象)。 - ?示例:
std::thread t1([]{ /* ... */ }); std::thread t2 = std::move(t1); // t1 變為空,t2 接管線程 t2.join();
這里要注意下面幾點:
- 移動后,原線程對象?
x
?不再關聯任何線程。- 用于將線程對象存入容器或轉移所有權:std::vector<std::thread> threads; threads.push_back(std::thread([]{ /* ... */ })); // 必須用移動語義
std::vector<std::thread> threads;
threads.push_back(std::thread([]{ /* ... */ })); // 必須用移動語義
?二.賦值運算符重載
1. 移動賦值運算符(Move Assignment)
thread& operator=(thread&& rhs) noexcept;
- ?關鍵行為:
- 當前對象會先釋放自己原本持有的線程資源(如果存在)。
- 接管?
rhs
?的線程所有權,rhs
?變為空線程對象?(不再關聯任何線程)。 - 操作是原子的,且標記為?
noexcept
(保證不拋出異常)。
- ?示例:
std::thread t1([]{ /* 任務 1 */ }); std::thread t2; t2 = std::move(t1); // t1 的線程所有權轉移給 t2,t1 變為空 t2.join();
- ?典型用途:
- 將線程對象存入容器(如?
std::vector<std::thread>
)。 - 動態管理線程所有權(如線程池)。
- 將線程對象存入容器(如?
?2. 拷貝賦值運算符(被刪除)?
thread& operator=(const thread&) = delete;
- ?作用:?禁止拷貝賦值?(編譯時報錯)。
- ?原因:
- 線程是獨占資源,拷貝會導致多個對象管理同一線程,引發重復?
join()
?或?detach()
。 - 保證線程對象的唯一所有權,避免資源管理沖突。
- 線程是獨占資源,拷貝會導致多個對象管理同一線程,引發重復?
- ?錯誤示例:
std::thread t1([]{ /* ... */ }); std::thread t2; t2 = t1; // 編譯錯誤!拷貝賦值被禁用
??三.線程等待和線程分離
????????線程等待就是讓主線程等待子線程執行完畢,首先我們要明白為什么要進行線程等待。
1.主線程是進程的入口,若主線程執行完畢,操作系統會直接終止整個進程(包括所有子線程),無論子線程是否完成任務。
2.子線程可能持有共享資源(如內存、文件句柄、網絡連接),若主線程不等待子線程結束就退出,可能導致:資源泄漏?(如未關閉的數據庫連接)和數據競爭?(子線程訪問已被主線程釋放的內存,導致崩潰)。
3.部分情況下線程執行計算或數據處理后,主線程需要匯總結果
????????線程分離就是用于解除線程與其創建者(如主線程)的關聯,使得子線程在終止后能夠自動釋放資源,無需其他線程顯式調用?join()
?等待或回收。
????????下面我們就來講解一下線程等待和線程分離函數join()
?和?detach()
join()
:在主線程中調用阻塞主線程,直到當前線程執行完畢,就是讓主線程等待子線程執行完畢
detach()
:分離線程,使其在后臺獨立運行(無法再管理)。
必須在?std::thread
?對象銷毀前調用?join()
?或?detach()
,否則程序終止。
std::thread t([]{ /* ... */ });if (t.joinable()) {t.join(); // 或 t.detach();
}
四.線程 ID 和命名
- ?
get_id()
:獲取線程唯一標識符。 - ?
std::this_thread
?命名空間提供當前線程操作。有get_id(),yield(),sleep_for()等操作。
std::thread t([]{ std::cout << "Thread ID: " << std::this_thread::get_id() << "\n";
});
std::cout << "Main thread ID: " << t.get_id() << "\n";
t.join();
五.線程中的同步機制
1. 互斥量?std::mutex
防止多個線程同時訪問共享資源。
std::mutex mtx;
int counter = 0;void safe_increment() {std::lock_guard<std::mutex> lock(mtx); // 自動加鎖解鎖counter++;
}std::thread t1(safe_increment);
std::thread t2(safe_increment);
t1.join(); t2.join();
2. 條件變量?std::condition_variable
用于線程間的條件同步。
std::mutex mtx;
std::condition_variable cv;
bool ready = false;void wait_thread() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return ready; }); // 等待條件滿足std::cout << "Ready!\n";
}void signal_thread() {std::this_thread::sleep_for(std::chrono::seconds(1));{std::lock_guard<std::mutex> lock(mtx);ready = true;}cv.notify_one(); // 通知等待線程
}std::thread t1(wait_thread);
std::thread t2(signal_thread);
t1.join(); t2.join();
六.實踐操作:
用兩個線程分別交替打印 1-100 的奇數和偶數,通過互斥鎖和條件變量實現交替輸出:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>using namespace std;int num = 1; // 共享計數器
mutex mtx; // 互斥鎖
condition_variable cv; // 條件變量
const int MAX_NUM = 100; // 最大值void print_odd() {while (true) {unique_lock<mutex> lock(mtx);// 等待條件:數字為奇數或超過最大值cv.wait(lock, [] { return (num % 2 == 1) || (num > MAX_NUM); });if (num > MAX_NUM) break;cout << "Odd: " << num << endl;num++; // 遞增到偶數lock.unlock();cv.notify_all(); // 喚醒另一個線程}
}void print_even() {while (true) {unique_lock<mutex> lock(mtx);// 等待條件:數字為偶數或超過最大值cv.wait(lock, [] { return (num % 2 == 0) || (num > MAX_NUM); });if (num > MAX_NUM) break;cout << "Even: " << num << endl;num++; // 遞增到奇數lock.unlock();cv.notify_all(); // 喚醒另一個線程}
}int main() {thread t1(print_odd);thread t2(print_even);t1.join();t2.join();return 0;
}
運行結果
????????c++的線程就講到這里,有幫助的話就點點贊吧。