C ++在析构函数中杀死一个线程
C++ Kill a Thread In Destructor
我有一个 class 启动另一个线程,该线程以固定的时间间隔访问它的一些数据。这意味着我有两个线程访问相同的数据(原始线程和新创建的线程)。这引入了对互斥量的需要。一切顺利,直到 class 的析构函数被调用(在程序末尾)并且内存位置不再有效。此时新线程尝试访问数据并得到一个访问冲突错误(很明显)。
我想做的是在析构函数中停止线程,或者让线程在“注意到”class 实例已被销毁后停止。
这是简化的线程代码(为简洁起见使用了 typedef):
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 有太多工作要做或 accessData 函数花费太多时间时,您将无法在 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
。
我有一个 class 启动另一个线程,该线程以固定的时间间隔访问它的一些数据。这意味着我有两个线程访问相同的数据(原始线程和新创建的线程)。这引入了对互斥量的需要。一切顺利,直到 class 的析构函数被调用(在程序末尾)并且内存位置不再有效。此时新线程尝试访问数据并得到一个访问冲突错误(很明显)。
我想做的是在析构函数中停止线程,或者让线程在“注意到”class 实例已被销毁后停止。
这是简化的线程代码(为简洁起见使用了 typedef):
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 有太多工作要做或 accessData 函数花费太多时间时,您将无法在 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
。