【问题标题】:QThread is creating a memory leakQThread 正在创建内存泄漏
【发布时间】:2014-03-02 23:46:48
【问题描述】:

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

【问题讨论】:

  • 您对QPointer 的使用毫无意义:)
  • 您使用的是哪个版本的 Qt? (QThread deleteLater() 行为在 4.8 左右发生了变化。)
  • 感谢更新和 cmets:打开 - QT 5.2.0

标签: multithreading qt memory memory-leaks qthread


【解决方案1】:

答案:

  • 为什么QThread* 在工作线程完成其工作时没有自行销毁?

因为有多种使用QThread的方法,其中至少一个必须能够在worker完成后查询线程状态或类成员。也可以重新启动线程。

  • 如何在工作完成时强制QThread* 删除线程和工作对象?

finished 信号应该足以让您调用一个槽来删除两者。

  • 是否应该以不同方式实现 QThread

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

【讨论】:

    【解决方案2】:
    1. QThread 并不知道它的工作何时完成。您可以通过思考如何实现这一点来回答自己的问题。

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

      也许您希望在没有更多QObjects 将该线程作为其线程时终止该线程。这当然可以作为QThread 中的可选行为来实现,但我不知道它是否会被接受。这不能是一种默认行为,因为在许多情况下,不断销毁和重新创建线程只是浪费 - 人们可能希望保留一个没有对象的线程。

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

    2. A 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 方法的线程没有被阻塞,并且有机会让 its 事件循环继续进行。正是这个事件循环将处理线程的删除。

      正如 AlexP 所指出的,这在 Qt 4.7 或更早版本中不起作用,因为所有这些版本在 QThread 的实现中都有错误。 “QThread 的行为发生了变化”是“有一个丑陋的 bug 终于修复了”的委婉说法。

    4. 您的连接过于冗长。您可以从签名中删除空格和引用/常量引用。第三个参数也是可选的,如果它是this。它应该看起来像:

      connect(worker,                                    
              SIGNAL(updateMessageFromThread(QString,QString,QString,QString,
                                             QString,QString,QString)),
              SLOT(updateStatusBarFromThread(QString,QString,QString,QString,
                                             QString,QString,QString)));
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      • 1970-01-01
      • 2016-12-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多