C++11——多线程编程15 如何在类中使用 std::thread 作为成员变量?

翻译来自: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

 

posted @ 2021-11-22 18:39  冰糖葫芦很乖  阅读(2787)  评论(0编辑  收藏  举报