In this article we will discuss how to use std::thread object as member variable inside class and its benefits.
As std::thread objects are move only, therefore while designing a class that use std::thread as member variable, we need to take care that objects of this class should also be move only.
Creating Move-only class with std::thread as member variable
Let’s create a ThreadWrapper class that has std::thread as member variable and make it move-able by,
- Deleting its copy constructor and assignment operator.
- Defining Move constructor and Move assignment operator.
/* * 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(); };
Its parameterized constructor will accept callback / function pointer / funcion object as argument that will be used as thread function by internal thread object i.e.
// Parameterized Constructor ThreadWrapper::ThreadWrapper(std::function<void()> func) : threadHandler(func) {}
Move Constructor & Assignment Operator
In Move constructor and assignment operator we need to move the thread object i.e.
// 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; }
In Move assignment operator we first need to join the current thread  object if its joinable, before replacing it with new thread object.
In the destructor of ThreadWrapper we need to join the thread object. It is necessary because if thread object is destructed without joining, then it will terminate the application i.e.
// Destructor : Join the thread object ThreadWrapper::~ThreadWrapper() { if (threadHandler.joinable()) threadHandler.join(); }
Now Let’s create a ThreadWrapper object, now when this object will be destructed, internal thread will be joined in destructor i.e
// 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 }
Also, we can create a vector of ThreadWraper i.e.
// 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));
We don’t even need to join the threads separately , it will be automatically joined when vector is destructed. We can also change the content of vector i.e.
ThreadWrapper thwp3(func); // Change the content of vector vecOfThreads[1] = std::move(thwp3);
The complete example is as follows,
#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; }
Output:
From Thread ID : 140646533629696 Move Constructor is called Move Constructor is called Move Constructor is called Move Assignment is called From Thread ID : 140646533629696 From Thread ID : 140646525236992 From Thread ID : 140646516844288