C++11线程库 (七) 线程的停止

在这篇文章里,我们将会讨论如何在C++11下停止和终止一个线程。

为什么C++11不直接提供一个停止线程的方法?这是因为线程在停止之前可能有一些资源需要被释放、关闭,比如说:

  • 线程中已经获取锁的所有权,突然退出谁将会负责这个锁所有权释放?
  • 如果线程打开了一写文件流,谁将会回收这个文件资源?
  • 如果线程中有动态内存分配,内存泄露如何避免?

简单来说就是线程退出时可能有资源需要回收,而C++11无法知晓用户具体使用了哪些资源。虽然如此,但是我们仍可以通过简单的方式实现线程退出:即定时检查或者程序中某个节点检查,检查的内容是是否退出,处理方式是释放所有的资源优雅退出。

一、使用std::future<>停止一个线程

我们可以向线程传递一个std::future<void>对象,为什么是传递一个空值,这是因为我们只是想要信号而不是真正的想要获取值。

首先创建一个值为void的promise对象:

std::promise<void> exitSignal;

现在我们可以在主线程中绑定promise和future:

std::future<void> futureObj=exitSignal.get_future();

然后在主函数中创建线程并往线程传递future对象:

std::thread th(&threadFunction,std::move(futureObj));

在线程的内部,除了完成一些线程工作外,还持续检查线程是否需要退出(在这里的条件就是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;
}

只要我们设置promise对象值,在线程中的promise将会收到信号已经准备就绪的信号:

exiSignal.set_value();

整个例子如下:

#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;
}

可以将上述过程理解为一个信号与槽,信号是std::promise,槽是线程函数中等待的信号的futureObj对象。 std::future<void> futureObj = exitSignal.get_future();则是绑定信号与槽的QObject::connected。

二、创建一个可以停止的线程

方法一的缺陷在于,我们需要重复创建promise和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 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();
    }
};

这样一来,创建一个可停止的任务只需要继承这个类并重写run方法即可:

/*
 * 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;
    }
};

现在让我们来看看如何使用这个可停止的线程任务:

// 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();

整个程序如下:

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/

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值