经过对我的QT应用程序的大量测试和更改后,Visual Leak Detector确定了一个讨厌的泄漏源(8个字节)。 VLD报告QT应用程序是干净的,除了QThread*
指针。
一些实现背景:应用程序被建模为Jeffrey Holmes Bulk download of web pages using Qt的混合解决方案。感谢Jeffrey的早期解决方案!
问题:
为什么QThread*
在工作线程完成工作时不会自行销毁?
如何在工作完成时强制QThread*
删除线程和工作线程对象?
QThread
应该以不同方式实施吗?
代码:
void vqMDIChildDialog::processWorkQueue(bool bIsBC)
{
if (m_listOfTables.isEmpty() && currentReplicationThreads == 0)
{
}
else if (!m_listOfTables.isEmpty())
{
for (int i = 0; i < maxReplicationThreads && !m_listOfTables.isEmpty();i++)
{
QThread *thread = new QThread;
QPointer<vcSharedDataQt> worker = new vcSharedDataQt();
worker->moveToThread(thread);
QString tmpTableName (m_listOfTables.dequeue());
worker->setParentObject(this);
//
// set properties on the worker object.
//
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread,SLOT(deleteLater()));
connect(worker,
SIGNAL(updateMessageFromThread( const QString&,
const QString&,
const QString&,
const QString&,
const QString&,
const QString&,
const QString&)),
this,
SLOT(UpdateStatusBarFromThread( const QString&,
const QString&,
const QString&,
const QString&,
const QString&,
const QString&,
const QString&)));
thread->setObjectName(worker->getUniqueKey());
thread->start();
currentReplicationThreads ++;
}
}
}
Stack不允许我回答这个问题:
该功能受QMutex
:
mutex.lock();
processWorkQueue();
mutex.unlock();
这导致内存泄漏。 QThread
显然无法在工作线程完成时销毁。我删除了互斥锁,VLD报告QThread
没有内存泄漏。
答案 0 :(得分:3)
QThread
并不知道其工作何时完成。您可以通过思考如何实现这一点来回答您自己的问题。
它的run()
方法只是旋转一个事件循环。由于所有事件循环都可以知道是否有任何事件发布到它,您可以合理实现的唯一条件是在没有其他事件时退出该线程。这会使线程立即退出,所以它根本没用。
当没有更多QObject
s将线程作为其线程时,您可能希望终止该线程。这肯定可以作为QThread
中的可选行为来实现,但是它是否是一个我不知道会被接受的变化。它不能是默认行为,因为在许多情况下,线程的持续破坏和重新创建简直就是浪费 - 人们可能希望保留一个没有对象的线程。
最终只有你知道线程的工作何时完成。您的工作者对象可以调用thead()->quit()
,或者它可以在完成时发出信号 - 就像您已经完成的那样。
工作完成后,QThread
无法自行销毁,因为线程可以重启。你可以完全控制线程的生命周期,所以你当然可以在工作完成时销毁它,而你已经完成了,只有你做错了。
您的问题实际上是您希望事情发生的顺序。 deleteLater
操作由事件循环执行。如果线程的事件循环没有运行,deleteLater
是NO-OP。
因此,首先,您的连接应该完成,以便它们形成一个只能以明确定义的顺序执行的级联:
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(worker, SIGNAL(destroyed()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
然后,您必须确保运行processWorkQueue
方法的线程未被阻止,并且其事件循环有可能继续。这个事件循环将处理线程的删除。
如AlexP所述,这在Qt 4.7或更早版本中不起作用,因为所有这些版本在QThread的实现中都存在错误。 “QThread的行为发生了变化”是一种委婉说法,“有一个丑陋的虫子最终被修复了”。
您的连接过于冗长。您可以从签名中删除空格和引用/ const引用。如果它是this
,则第三个参数也是可选的。它应该看起来像:
connect(worker,
SIGNAL(updateMessageFromThread(QString,QString,QString,QString,
QString,QString,QString)),
SLOT(updateStatusBarFromThread(QString,QString,QString,QString,
QString,QString,QString)));
答案 1 :(得分:0)
数目:
QThread*
不会自行销毁?因为有不同的方法可以使用QThread
,其中至少有一个方法必须能够在worker完成后查询线程状态或类成员。线程也可以重新启动。
QThread*
删除线程和工作线程对象? finished
信号应足以让您拨打删除两者的插槽。
QThread
应该以不同的方式实施吗?针对不同情况有不同的实现方式。 http://qt-project.org/doc/qt-5.0/qtcore/thread-basics.html