C++11 thread library thread stop

Posted by akravets on Fri, 29 Oct 2021 04:32:19 +0200

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/

Topics: C++