In this article, we will discuss how to stop and terminate a thread under C++11.
Why doesn't C++11 directly provide a method to stop threads? This is because some resources may need to be released and closed before the thread stops, such as:
- The thread has acquired the ownership of the lock. Who will be responsible for releasing the ownership of the lock when it suddenly exits?
- If a thread opens a write file stream, who will recycle the file resource?
- If there is dynamic memory allocation in the thread, how to avoid memory leakage?
In short, there may be resources to be recycled when the thread exits, and C++11 cannot know what resources the user has. Nevertheless, we can still realize thread exit in a simple way: regular check or check a node in the program. The check content is whether to exit. The processing method is to release all resources and exit gracefully.
1, Use STD:: future < > to stop a thread
We can pass an STD:: future < void > object to the thread. Why do we pass a null value? This is because we just want the signal, not the real value.
First, create a promise object with a value of void:
std::promise<void> exitSignal;
Now we can bind promise and future in the main thread:
std::future<void> futureObj=exitSignal.get_future();
Then create a thread in the main function and pass the future object to the thread:
std::thread th(&threadFunction,std::move(futureObj));
Inside the thread, in addition to completing some thread work, it also continuously checks whether the thread needs to exit (the condition here is whether the future object value is available)
void threadFunction(std::future<void> futureObj) { std::cout << "Thread Start" << std::endl; while (futureObj.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout) { std::cout << "Doing Some Work" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } std::cout << "Thread End" << std::endl; }
As long as we set the promise object value, promise in the thread will receive a signal that the signal is ready:
exiSignal.set_value();
The whole example is as follows:
#include <thread> #include <iostream> #include <assert.h> #include <chrono> #include <future> void threadFunction(std::future<void> futureObj) { std::cout << "Thread Start" << std::endl; while (futureObj.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout) { std::cout << "Doing Some Work" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } std::cout << "Thread End" << std::endl; } int main() { // Create a std::promise object std::promise<void> exitSignal; //Fetch std::future object associated with promise std::future<void> futureObj = exitSignal.get_future(); // Starting Thread & move the future object in lambda function by reference std::thread th(&threadFunction, std::move(futureObj)); //Wait for 10 sec std::this_thread::sleep_for(std::chrono::seconds(10)); std::cout << "Asking Thread to Stop" << std::endl; //Set the value in promise exitSignal.set_value(); //Wait for thread to join th.join(); std::cout << "Exiting Main Function" << std::endl; return 0; }
The above process can be understood as a signal and slot. The signal is std::promise, and the slot is the futureobj object of the signal waiting in the thread function. std::future<void> futureObj = exitSignal.get_ future(); The QObject::connected of the binding signal and the slot.
2, Create a thread that can be stopped
The drawback of method 1 is that we need to repeatedly create promise and future multiple times in order to implement an exitable thread.
We can avoid this trouble with the help of object-oriented method.
/* * Class that encapsulates promise and future object and * provides API to set exit signal for the thread */ class Stoppable { std::promise<void> exitSignal; std::future<void> futureObj; public: Stoppable() : futureObj(exitSignal.get_future()) { } Stoppable(Stoppable && obj) : exitSignal(std::move(obj.exitSignal)), futureObj(std::move(obj.futureObj)) { std::cout << "Move Constructor is called" << std::endl; } Stoppable & operator=(Stoppable && obj) { std::cout << "Move Assignment is called" << std::endl; exitSignal = std::move(obj.exitSignal); futureObj = std::move(obj.futureObj); return *this; } // Task need to provide definition for this function // It will be called by thread function virtual void run() = 0; // Thread function to be executed by thread void operator()() { run(); } //Checks if thread is requested to stop bool stopRequested() { // checks if value in future object is available if (futureObj.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout) return false; return true; } // Request the thread to stop by setting value in promise object void stop() { exitSignal.set_value(); } };
In this way, to create a stoppable task, you only need to inherit this class and override the run method:
/* * A Task class that extends the Stoppable Task */ class MyTask: public Stoppable { public: // Function to be executed by thread function void run() { std::cout << "Task Start" << std::endl; // Check if thread is requested to stop ? while (stopRequested() == false) { std::cout << "Doing Some Work" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } std::cout << "Task End" << std::endl; } };
Now let's look at how to use this stoppable thread task:
// Creating our Task MyTask task; //Creating a thread to execute our task std::thread th([&]() { task.run(); }); std::this_thread::sleep_for(std::chrono::seconds(10)); std::cout << "Asking Task to Stop" << std::endl; // Stop the Task task.stop(); //Waiting for thread to join th.join();
The whole procedure is as follows:
include <thread> #include <iostream> #include <assert.h> #include <chrono> #include <future> /* * Class that encapsulates promise and future object and * provides API to set exit signal for the thread */ class Stoppable { std::promise<void> exitSignal; std::future<void> futureObj; public: Stoppable() : futureObj(exitSignal.get_future()) { } Stoppable(Stoppable && obj) : exitSignal(std::move(obj.exitSignal)), futureObj(std::move(obj.futureObj)) { std::cout << "Move Constructor is called" << std::endl; } Stoppable & operator=(Stoppable && obj) { std::cout << "Move Assignment is called" << std::endl; exitSignal = std::move(obj.exitSignal); futureObj = std::move(obj.futureObj); return *this; } // Task need to provide defination for this function // It will be called by thread function virtual void run() = 0; // Thread function to be executed by thread void operator()() { run(); } //Checks if thread is requested to stop bool stopRequested() { // checks if value in future object is available if (futureObj.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout) return false; return true; } // Request the thread to stop by setting value in promise object void stop() { exitSignal.set_value(); } }; /* * A Task class that extends the Stoppable Task */ class MyTask: public Stoppable { public: // Function to be executed by thread function void run() { std::cout << "Task Start" << std::endl; // Check if thread is requested to stop ? while (stopRequested() == false) { std::cout << "Doing Some Work" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } std::cout << "Task End" << std::endl; } }; int main() { // Creating our Task MyTask task; //Creating a thread to execute our task std::thread th([&]() { task.run(); }); std::this_thread::sleep_for(std::chrono::seconds(10)); std::cout << "Asking Task to Stop" << std::endl; // Stop the Task task.stop(); //Waiting for thread to join th.join(); std::cout << "Thread Joined" << std::endl; std::cout << "Exiting Main Function" << std::endl; return 0; }
[1] https://thispointer.com/c11-how-to-stop-or-terminate-a-thread/