翻譯來自:https://thispointer.com/c11-how-to-use-stdthread-as-a-member-variable-in-class/
在本文中,我們將討論如何在類中使用 std::thread 對象作為成員變量及其好處。
由於 std::thread 對象只能移動,因此在設計一個使用 std::thread 作為成員變量的類時,我們需要注意該類的對象也應該是只能移動的。
使用 std::thread 作為成員變量創建 Move-only 類
讓我們創建一個ThreadWrapper類,它具有std::thread作為成員變量,並使其可移動,
- 刪除其復制構造函數和賦值運算符。
- 定義 Move 構造函數和 Move 賦值運算符。
/* * 一個以線程對象作為成員變量的類 */ class ThreadWrapper { // std::thread 對象 std::thread threadHandler; public: //刪除復制構造函數 ThreadWrapper(const ThreadWrapper&) = delete; //刪除賦值操作符 ThreadWrapper& operator = (const ThreadWrapper&) = delete; // 參數化構造函數 ThreadWrapper(std::function < void()> func); // 移動構造函數 ThreadWrapper(ThreadWrapper&& obj); //移動賦值運算符 ThreadWrapper& operator= (ThreadWrapper && obj); //析構函數 ~ThreadWrapper(); };
它的參數化構造函數將接受回調/函數指針/函數對象作為參數,這些參數將被內部線程對象用作線程函數,即
ThreadWrapper:: ThreadWrapper ( std::function < void ()> func ) : threadHandler ( func ){}
移動構造函數和賦值運算符
在移動構造函數和賦值運算符中,我們需要移動線程對象,即
// 移動構造函數 ThreadWrapper::ThreadWrapper(ThreadWrapper&& obj) :threadHandler(std::move(obj.threadHandler)) { std::cout << "Move Constructor is called" << std::endl; } //移動賦值運算符 ThreadWrapper& ThreadWrapper::operator=(ThreadWrapper&& obj) { std::cout << "Move Assignment is called" << std::endl; if (threadHandler.joinable()) threadHandler.join(); threadHandler = std::move(obj.threadHandler); return *this; }
在移動賦值運算符中,我們首先需要加入當前線程對象(如果它是可連接的),然后再用新的線程對象替換它。
在ThreadWrapper的析構函數中我們需要加入線程對象。這是必要的,因為如果線程對象在沒有加入的情況下被破壞,那么它將終止應用程序即
// Destructor : Join the thread object ThreadWrapper::~ThreadWrapper() { if (threadHandler.joinable()) threadHandler.join(); }
現在讓我們創建一個 ThreadWrapper 對象,現在當這個對象被析構時,內部線程將加入析構函數即
// 創建一個 std::function 對象 std::function<void()> func = []() { // 休眠 1 秒 std::this_thread::sleep_for(std::chrono::seconds(1)); // 打印線程ID std::cout << "From Thread ID : " << std::this_thread::get_id() << "\n"; }; { // 創建一個 ThreadWrapper 對象 // 它會在內部啟動線程 ThreadWrapper wrapper(func); //當包裝器超出范圍時,將調用其析構函數 // 這將在內部加入成員線程對象 }
此外,我們可以創建一個 ThreadWraper 向量,即
// 創建一個 ThreadWrapper 對象的向量 std::vector < ThreadWrapper > vecOfThreads;
// 在線程中添加 ThreadWrapper 對象
ThreadWrapper thwp1 ( func ) ;
ThreadWrapper thwp2 ( func ) ;
vecOfThreads。push_back ( std:: move ( thwp1 )) ;
vecOfThreads。push_back ( std:: move ( thwp2 )) ;
我們甚至不需要單獨加入線程,當vector被破壞時它會自動加入。我們也可以改變vector的內容,即
ThreadWrapper thwp3 ( func ) ;
// 改變vector的內容
vecOfThreads [ 1 ] = std:: move ( thwp3 );
#include <thread> #include <mutex> #include <vector> #include <iostream> #include <assert.h> #include <chrono> /* * A class that has thread object as member variable */ class ThreadWrapper { // std::thread object std::thread threadHandler; public: //Delete the copy constructor ThreadWrapper(const ThreadWrapper&) = delete; //Delete the Assignment opeartor ThreadWrapper& operator=(const ThreadWrapper&) = delete; // Parameterized Constructor ThreadWrapper(std::function<void()> func); // Move Constructor ThreadWrapper(ThreadWrapper && obj); //Move Assignment Operator ThreadWrapper & operator=(ThreadWrapper && obj); //Destructor ~ThreadWrapper(); }; // Parameterized Constructor ThreadWrapper::ThreadWrapper(std::function<void()> func) : threadHandler(func) {} // Move Constructor ThreadWrapper::ThreadWrapper(ThreadWrapper && obj) : threadHandler(std::move(obj.threadHandler)) { std::cout << "Move Constructor is called" << std::endl; } //Move Assignment Operator ThreadWrapper & ThreadWrapper::operator=(ThreadWrapper && obj) { std::cout << "Move Assignment is called" << std::endl; if (threadHandler.joinable()) threadHandler.join(); threadHandler = std::move(obj.threadHandler); return *this; } // Destructor ThreadWrapper::~ThreadWrapper() { if (threadHandler.joinable()) threadHandler.join(); } int main() { // Creating a std::function object std::function<void()> func = []() { // Sleep for 1 second std::this_thread::sleep_for (std::chrono::seconds(1)); // Print thread ID std::cout << "From Thread ID : " << std::this_thread::get_id() << "\n"; }; { // Create a ThreadWrapper object // It will internally start the thread ThreadWrapper wrapper(func); //When wrapper will go out of scope, its destructor will be called // Which will internally join the member thread object } // Create a vector of ThreadWrapper objects std::vector<ThreadWrapper> vecOfThreads; // Add ThreadWrapper objects in thread ThreadWrapper thwp1(func); ThreadWrapper thwp2(func); vecOfThreads.push_back(std::move(thwp1)); vecOfThreads.push_back(std::move(thwp2)); ThreadWrapper thwp3(func); // Change the content of vector vecOfThreads[1] = std::move(thwp3); //When vector will go out of scope, its destructor will be called, which will // internally call the destructor all ThreadWrapper objects , which in turn // joins the member thread object. return 0; }
From Thread ID : 16312 Move Constructor is called Move Constructor is called Move Constructor is called Move Assignment is called From Thread ID : 21792 From Thread ID : 19224 From Thread ID : 30452