C++在析构函数中杀死一个线程



我有一个类,它启动另一个线程,以恒定的间隔访问它的一些数据。这意味着我有两个线程访问相同的数据(原始线程和新创建的线程(。这引入了对互斥的需求。在调用类的析构函数(在程序结束时(并且内存位置不再有效之前,一切都很顺利。此时,新线程尝试访问数据,并得到访问冲突错误(显然(。

我想做的是停止析构函数中的线程,或者让线程在";"通知";类实例已被销毁。

以下是简化的线程代码(为简洁起见,使用typedefs(:

void myClass::StartThread() {
auto threadFunc = [&, this]() {
while (true) {
time_point now = steady_clock::now();
if (chro::duration_cast<chro::milliseconds>(now - this->m_lastSeedTime).count() > INTERVAL) {
std::lock_guard<std::mutex> lockGuard(this->m_mut);
this->m_lastSeedTime = now;
this->accessData();
}
}
};
std::thread thread(threadFunc);
thread.detach();

当然,如果我只是以某种明显的方式处理不当,请也告诉我。

如果你想让线程死亡,你应该让它退出。这是唯一可靠的干净方法。

只需更改

while (true)

while(this->keepRunning)

并将其适当地同步。要么不要分离线程(这样析构函数就可以加入它(,要么为线程添加某种方式来指示它已经退出(这样析构函数就可以等待它(。

哦,与其旋转,线程可能应该休眠。在这种情况下,如果你不希望析构函数也阻塞,你需要一些方法来中断睡眠:对睡眠的条件变量使用定时等待可以让这变得很容易。

@Useless'的答案是正确的。以下是具体的操作方法:

class myClass{
...
private:
std::thread m_thread;
std::atomic_bool m_keepRunning{true};
....
};

void myClass::StartThread() {
auto threadFunc = [&, this]() {
while (m_keepRunning) {
time_point now = steady_clock::now();
if (chro::duration_cast<chro::milliseconds>(now - this->m_lastSeedTime).count() > INTERVAL) {
std::lock_guard<std::mutex> lockGuard(this->m_mut);
if(!m_keepRunning) break; // destructor called, don't access data
this->m_lastSeedTime = now;
this->accessData();
}
}
};
m_thread = std::thread(threadFunc);
}
myClass::~myClass()
{
m_keepRunning = false;
m_mutex.unlock(); // make sure we don't wait in the loop for the lock
if(m_thread.joinable()) m_thread.join();

// do other cleaning
}

另一点是,当你总是等待INTERVAL时,它会导致时间的累积延迟。假设您的间隔为50毫秒。当您的CPU有太多的工作要做或访问Data函数花费太多时间时,您将无法在50毫秒内运行下一次迭代。假设它将是52毫秒,即2毫秒的延迟。这些延迟会随着时间的推移而增加,并会影响您的精度。

相反,你可以做:

time_point waitUntil = steady_clock::now() + initialWaitTime;
while(m_keepRunning){
if(steady_clock::now() >= waitUntil)
{
// ... do your work
waitUntil = waitUntil + chro::milliseconds(INTERVAL)
}
}

对于定时等待部分,@Useless也是正确的。旋转会给你的核心带来沉重的负担。相反,您应该使用条件互斥或timed_mutex。但上述建议仍然有效。不要使用sleep_for,而是使用sleep_until

终止线程不起作用。问题是,如果您确实终止了一个线程,它可能处于应该作为原子操作执行的多步操作的中间,从而使您的程序处于无效状态。相反,向另一个线程发出自杀的信号,然后等待它死去。

最新更新