问题
After much testing and changes to my QT application, Visual Leak Detector identified the source of a pesky leak (8 bytes). VLD reported the QT application is clean except for a QThread*
pointer.
A bit of implementation background: the application is modelled as a hybrid of solution by Jeffrey Holmes, Bulk download of web pages using Qt. Thanks to Jeffrey for the earlier solution!
Questions:
Why is
QThread*
not destroying itself when the worker thread completed its work?How can I force the
QThread*
to delete the thread and worker object when work is complete?Should
QThread
be implemented differently?
Code:
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 would not permit me to answer this question so:
The function was protected with a QMutex
:
mutex.lock();
processWorkQueue();
mutex.unlock();
This caused the memory leak. QThread
apparently could not be destroyed on worker thread completion. I removed the mutex and VLD is reporting no memory leaks with QThread
.
回答1:
A
QThread
doesn't really know when its work is done. You can answer your own question by thinking of how you would implement this.Its
run()
method simply spins an event loop. Since all an event loop can know is if there are any events posted to it, the only condition you could reasonably implement is to quit the thread when there are no further events. This would make a thread quit immediately, so it's not helpful at all.Perhaps you'd wish to terminate the thread when there are no more
QObject
s that have the thread as their thread. This could certainly be implemented as an optional behavior inQThread
, but whether it's a change that would get accepted I don't know. It must not be a default behavior, since in many situations constant destruction and re-creation of threads is simply wasteful - one might want to retain a thread that has no objects on it.Ultimately only you know when the thread's work is done. Your worker object can invoke
thead()->quit()
or it can emit a signal when it's done - like you do already.A
QThread
cannot destroy itself when the work is done, since threads are restartable. You have full control over thread's lifetime, so you certainly can destroy it when its work is done, and you do already, only you do it wrong.Your problem is really in the order that you wish for things to happen. The
deleteLater
operation is performed by the event loop. If a thread's event loop isn't running, thedeleteLater
is a NO-OP.So, first of all, your connections should be done such that they form a cascade that can only execute in a well-defined order:
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()));
Then, you must ensure that the thread where your
processWorkQueue
method runs is not blocked and has a chance for its event loop to proceed. It's this event loop that will process thread's deletion.As noted by AlexP, this doesn't work in Qt 4.7 or earlier, since all those versions had a bug in QThread's implementation. "QThread's behavior has changed" is an euphemism for "there was an ugly bug that finally fixed".
Your connect is overly verbose. You can drop spaces and reference/const-reference from the signature. The third argument is also optional if it's
this
. It should look like:connect(worker, SIGNAL(updateMessageFromThread(QString,QString,QString,QString, QString,QString,QString)), SLOT(updateStatusBarFromThread(QString,QString,QString,QString, QString,QString,QString)));
回答2:
Answers:
- Why is
QThread*
not destroying itself when the worker thread completed its work?
Because there are different ways to use QThread
in which at least one must be able to query thread states or class members after worker has finished. Also the thread may be restarted.
- How can I force the
QThread*
to delete the thread and worker object when work is complete?
The finished
signal should be enough for you to call a slot deleting both.
- Should
QThread
be implemented differently?
There are different implementations for different situations. http://qt-project.org/doc/qt-5.0/qtcore/thread-basics.html
来源:https://stackoverflow.com/questions/21596000/qthread-is-creating-a-memory-leak