在这篇文章里,我们将会讨论如何在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()
{
std::promise<void> exitSignal;
std::future<void> futureObj = exitSignal.get_future();
std::thread th(&threadFunction, std::move(futureObj));
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "Asking Thread to Stop" << std::endl;
exitSignal.set_value();
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 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;
}
virtual void run() = 0;
void operator()()
{
run();
}
bool stopRequested()
{
if (futureObj.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
return false;
return true;
}
void stop()
{
exitSignal.set_value();
}
};
这样一来,创建一个可停止的任务只需要继承这个类并重写run方法即可:
class MyTask: public Stoppable
{
public:
void run()
{
std::cout << "Task Start" << std::endl;
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;
}
};
现在让我们来看看如何使用这个可停止的线程任务:
MyTask task;
std::thread th([&]()
{
task.run();
});
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "Asking Task to Stop" << std::endl;
task.stop();
th.join();
整个程序如下:
include <thread>
#include <iostream>
#include <assert.h>
#include <chrono>
#include <future>
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;
}
virtual void run() = 0;
void operator()()
{
run();
}
bool stopRequested()
{
if (futureObj.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
return false;
return true;
}
void stop()
{
exitSignal.set_value();
}
};
class MyTask: public Stoppable
{
public:
void run()
{
std::cout << "Task Start" << std::endl;
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()
{
MyTask task;
std::thread th([&]()
{
task.run();
});
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "Asking Task to Stop" << std::endl;
task.stop();
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/
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)