使用对象生存期运行线程

Run thread with object lifetime

本文关键字:运行 线程 生存期 对象      更新时间:2023-10-16

在我的类A中,有一个线程的运行时间与对象的生存期一样长。现在,我有一个布尔成员变量,它在每个循环中都会被检查,在析构函数中,这个变量被设置为false。

class A {
public:
A() : mRun(true) {
mThread = std::thread(&A::DoWork(), this);
}
~A() {
mRun = false;
}
private:
bool        mRun;
std::thread mThread;
void DoWork() {
while (mRun) {
...
}
}
};

是否可以安全地使用while(true)?我读到,在销毁线程时,它们将被终止。

"是否可以安全地使用while(true)?">

是的(假设while(true)实际上意味着while (mRun))。您需要确保mRun成员对于来自不同线程的并发读/写访问是安全的。最简单的方法是使用std::atomic<>值,如下所示:

class A {
public:
A() : mRun(true) {
mThread = std::thread(&A::DoWork(), this);
}
~A() {
mRun = false; // <<<< Signal the thread loop to stop
mThread.join(); // <<<< Wait for that thread to end
}
private:
std::atomic<bool>        mRun; // Use a race condition safe data 
// criterium to end that thread loop
std::thread mThread;
void DoWork() {
while (mRun == true) {
...
}
}
};

其中CCD_ 7应当回退到CCD_。

要直接解释如何使OP的问题线程安全,请阅读第一段。其余部分描述了另一种处理退出条件的方法

是的,您可以在代码中安全地使用while(mRun)。为了使现有的示例线程安全,请调用mthread.join(),这样线程就不会在线程仍在运行时执行其析构函数。


控制线程执行的另一种方法是使用异常和异常处理程序来转义线程循环。这是通过在循环外使用try-catch语句来实现的。这是boost::thread使用的方法,因此在您的方法中,您将调用my_thread.interrupt_check(),这将引发异常。这将允许堆栈展开,并销毁堆栈上的任何对象。但是你必须小心使用这种方法。如果线程中没有捕捉到异常,程序将调用std::terminate,整个程序将停止。这种方法的一个例子是这样的:

void interrupt_check();
class interruptible_thread
{
private:
static thread_local std::atomic<bool>* interrupt_flag;
friend void interrupt_check();
std::atomic<bool> flag;
std::thread thread_obj;
struct interrupt_exception { };
public:
template<typename _Fn, typename... _Args>
interruptible_thread(_Fn& function, _Args& arguments) :
flag(false),
thread_obj([](std::atomic<bool>* flag_var, _Fn&& function, _Args&& arguments)
{ interrupt_flag = flag_var; 
try
{
function(std::forward<_Args>(arguments));
} catch (interrupt_exception& e) { }
},
&interrupt_flag,
std::forward<_Fn>(function),
std::forward<_Args>(arguments))
{ }
void interrupt() { flag = true; }
~interruptible_thread() { flag = false; interrupt(); thread_obj.join(); }
}
void interrupt_check()
{
if(!interruptible_thread::interrupt_flag)
throw interruptible_thread::interrupt_exception;
}

此方法的优点是可以正确执行析构函数,并且可以在调用方希望时终止线程。缺点是,如果线程不进行检查,那么这只是具有更多开销的std::thread

还要注意,如果std::thread对象被破坏,而线程仍然是可连接的,则调用std::terminate。我想这并不是将线包裹在物体中的意图。

您必须记住,mRun只会设置为false,因为对象正在被销毁。因为它是在自己的线程上,这不是一个好主意。一个更好的想法是使用一个已设置的全局布尔值,该类可以对其使用原子操作,以防止两个线程对其进行写入(例如,线程A将其设置为FALSE,而线程B在其正后方,并将其设置回TRUE,无论出于何种原因——坏主意…),这样,当对象被破坏时,您可以检测到它不再存在,而不尝试访问它。

当我做这样的事情时,我倾向于将线程本身的实例传递到我的RUN函数中,这样我就可以像一样访问它

while(threadInstance->mRun)

但这可能对你不起作用。