简体   繁体   English

QThread正在创建内存泄漏

[英]QThread is creating a memory leak

After much testing and changes to my QT application, Visual Leak Detector identified the source of a pesky leak (8 bytes). 经过大量测试并更改了我的QT应用程序后,Visual Leak Detector识别出了令人讨厌的泄漏源(8个字节)。 VLD reported the QT application is clean except for a QThread* pointer. VLD报告说,除QThread*指针外,QT应用程序是干净的。

A bit of implementation background: the application is modelled as a hybrid of solution by Jeffrey Holmes, Bulk download of web pages using Qt . 一些实施背景:Jeffrey Holmes将应用程序建模为解决方案的混合体, 使用Qt批量下载网页 Thanks to Jeffrey for the earlier solution! 感谢Jeffrey提早提供解决方案!

Questions: 问题:

  • Why is QThread* not destroying itself when the worker thread completed its work? 为什么工作线程完成工作后QThread*不会自我破坏?

  • How can I force the QThread* to delete the thread and worker object when work is complete? 工作完成后,如何强制QThread*删除线程和辅助对象?

  • Should QThread be implemented differently? QThread应该以不同的方式实现吗?

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: Stack不允许我回答这个问题,所以:

The function was protected with a QMutex : 该功能受QMutex保护:

mutex.lock();
processWorkQueue();
mutex.unlock();

This caused the memory leak. 这导致内存泄漏。 QThread apparently could not be destroyed on worker thread completion. QThread显然无法在辅助线程完成时销毁。 I removed the mutex and VLD is reporting no memory leaks with QThread . 我删除了互斥锁,并且VLD报告QThread没有内存泄漏。

  1. A QThread doesn't really know when its work is done. QThread并不真正知道何时完成工作。 You can answer your own question by thinking of how you would implement this. 您可以通过考虑如何实现此问题来回答自己的问题。

    Its run() method simply spins an event loop. 它的run()方法只是旋转事件循环。 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. 也许您希望在没有更多以该线程为线程的QObject时终止该线程。 This could certainly be implemented as an optional behavior in QThread , but whether it's a change that would get accepted I don't know. 当然可以在QThread中将其实现为可选行为,但是我不知道这是否会被接受。 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. 您的工作程序对象可以调用thead()->quit()或完成后发出信号-就像您已经做过的那样。

  2. A QThread cannot destroy itself when the work is done, since threads are restartable. 由于线程是可重新启动的,因此在完成工作后, QThread无法销毁自己。 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. 您可以完全控制线程的生存期,因此您可以肯定地在线程工作完成后销毁它,而您已经这样做了,只有错了。

  3. Your problem is really in the order that you wish for things to happen. 您的问题实际上是您希望事情发生的顺序。 The deleteLater operation is performed by the event loop. deleteLater操作由事件循环执行。 If a thread's event loop isn't running, the deleteLater is a NO-OP. 如果线程的事件循环未运行,则deleteLater是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. 然后,您必须确保运行processWorkQueue方法的线程未被阻塞,并且有机会继续事件循环。 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. 正如AlexP所指出的那样,这在Qt 4.7或更早的版本中不起作用,因为所有这些版本在QThread的实现中均存在错误。 "QThread's behavior has changed" is an euphemism for "there was an ugly bug that finally fixed". “ QThread的行为已更改”是委婉说法,因为“有一个丑陋的错误终于解决了”。

  4. Your connect is overly verbose. 您的连接过于冗长。 You can drop spaces and reference/const-reference from the signature. 您可以从签名中删除空格和引用/ const-reference。 The third argument is also optional if it's this . 如果是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))); 

Answers: 答案:

  • Why is QThread* not destroying itself when the worker thread completed its work? 为什么工作线程完成工作后QThread*不会自我破坏?

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. 因为有多种使用QThread ,其中在工作人员完成后至少必须有一种能够查询线程状态或类成员的方法。 Also the thread may be restarted. 此外,线程可能会重新启动。

  • How can I force the QThread* to delete the thread and worker object when work is complete? 工作完成后,如何强制QThread*删除线程和辅助对象?

The finished signal should be enough for you to call a slot deleting both. finished信号应该足以让您调用删除两者的插槽。

  • Should QThread be implemented differently? QThread应该以不同的方式实现吗?

There are different implementations for different situations. 针对不同情况有不同的实现。 http://qt-project.org/doc/qt-5.0/qtcore/thread-basics.html http://qt-project.org/doc/qt-5.0/qtcore/thread-basics.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM