QThread正在创建内存泄漏

时间:2014-02-06 06:51:42

标签: multithreading qt memory memory-leaks qthread

经过对我的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没有内存泄漏。

2 个答案:

答案 0 :(得分:3)

  1. QThread并不知道其工作何时完成。您可以通过思考如何实现这一点来回答您自己的问题。

    它的run()方法只是旋转一个事件循环。由于所有事件循环都可以知道是否有任何事件发布到它,您可以合理实现的唯一条件是在没有其他事件时退出该线程。这会使线程立即退出,所以它根本没用。

    当没有更多QObject s将线程作为其线程时,您可能希望终止该线程。这肯定可以作为QThread中的可选行为来实现,但是它是否是一个我不知道会被接受的变化。它不能是默认行为,因为在许多情况下,线程的持续破坏和重新创建简直就是浪费 - 人们可能希望保留一个没有对象的线程。

    最终只有你知道线程的工作何时完成。您的工作者对象可以调用thead()->quit(),或者它可以在完成时发出信号 - 就像您已经完成的那样。

  2. 工作完成后,QThread无法自行销毁,因为线程可以重启。你可以完全控制线程的生命周期,所以你当然可以在工作完成时销毁它,而你已经完成了,只有你做错了。

  3. 您的问题实际上是您希望事情发生的顺序。 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的行为发生了变化”是一种委婉说法,“有一个丑陋的虫子最终被修复了”。

  4. 您的连接过于冗长。您可以从签名中删除空格和引用/ 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