QThread finished() 连接到 QObject 的删除器

Posted

技术标签:

【中文标题】QThread finished() 连接到 QObject 的删除器【英文标题】:QThread finished() connected to deletelater of a QObject 【发布时间】:2012-08-20 07:48:08 【问题描述】:

在这里问这个问题之前,我想了很多,阅读了很多文章。没有一篇文章给了我正确的答案。

http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

 QThread* thread = new QThread;
 Worker* worker = new Worker();
 worker->moveToThread(thread);
 connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
 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()));
 thread->start();

Worker 对象具有新线程的亲和性。

    Worker 完成信号将在线程上调用 quit()。这将结束线程的事件循环并启动线程完成信号。

    Worker finished 信号连接到worker deleteLater()。根据deleteLater()文档

计划删除此对象。 当控制返回事件循环时,该对象将被删除。 如果事件循环没有运行 调用此函数时(例如,deleteLater()QCoreApplication::exec()) 之前的对象,该对象将被删除 一旦事件循环开始。

注意进入和离开一个新的 事件循环(例如,通过打开模式对话框)不会执行 延迟删除;对于要删除的对象,控件必须 返回到调用 deleteLater() 的事件循环。

注意:它 多次调用此函数是安全的;当第一次延期 删除事件被传递,对象的任何未决事件都是 从事件队列中移除。**

所以当没有事件循环时,由于线程已经退出并且它已经发出了完成信号,我们将不再再次启动同一个线程。在这种情况下,deleteLater() 将永远不会被处理,因为事件循环不存在并且工作对象根本不会被删除。这不会造成内存泄漏吗?

 connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
 connect(worker, SIGNAL(finished()), thread, SLOT(quit()));

如果我们认为交换两条线可以解决问题,那么我还有另一个问题。 QT 明确指出发出信号时调用槽的顺序是不确定的

上面提到的文章链接中有一堆cmets。连作者都无法完整回答这个问题

【问题讨论】:

【参考方案1】:

QThread 将在发送完成信号后执行一个事件类型为 QEvent::DeferredDelete 的QCoreApplication::sendPostedEvents

也就是说QThread会收集所有待处理的deleteLater,并在run返回后执行

来源:https://qt.gitorious.org/qt/qtbase/source/c657bb7b51115d6e1719166fb502bb0ca1dcf4e8:src/corelib/thread/qthread_win.cpp#L363-462

【讨论】:

您的链接(源链接)无效。但正如doc 中所述:“当此信号 [finished] 发出时,事件循环已经停止运行。线程中将不再处理任何事件,除了延迟删除事件。这个信号可以连接到 QObject::deleteLater(),以释放该线程中的对象。"【参考方案2】:
//! put the following code in constructor
QThread *thread = new QThread;
//! type of m_weakThread is QWeakPointer<QThread>
m_weakThread = thread;
Worker *worker = new Worker;
//! type of m_weakWorker is QWeakPointer<Worker>
m_weakWorker = worker;
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
//! instead of finished() signal, connect destroyed() signal to thread's quit() slot
connect(worker, SIGNAL(destroyed()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();

//! put the following code in destructor
if (!m_weakThread.isNull()) 
    QThread *thread = m_weakThread.data();
    if (thread->isRunning()) 
        thread->quit();
        thread->wait();
    

if (!m_weakWorker.isNull()) 
    Worker *worker = m_weakWorker.data();
    m_weakWorker.clear();   //! optional, a little optimization
    //! it's safe to release worker since the secondary thread exits
    delete worker;

if (!m_weakThread.isNull()) 
    QThread *thread = m_weakThread.data();
    m_weakThread.clear();
    //! it's safe to release thread since it exits and all objects in it has released
    delete thread;

【讨论】:

不能在创建的对象线程中删除worker。因为它已经使用 moveToThread 移动到了线程。你能解释一下吗? 另外我建议将父母分配给QThread。由于QThread 的实例是产生它的线程的一部分(与已移动到它的任何对象或其run() 方法不同),如果thread 要成为一部分,则执行thread = new QThread(this); 是完全安全的其他类的。一般来说,如果有更好的非手动解决方案,您应该避免致电delete。即使在标准 C++ 中,您也有智能指针,而没有什么可以减轻您手动清理的负担。

以上是关于QThread finished() 连接到 QObject 的删除器的主要内容,如果未能解决你的问题,请参考以下文章

如果 QThread 的完成信号连接到 deleteLater,是不是有必要删除在 QThread 上运行的对象?

QThread 完成状态

我也应该连接到 QNetworkReply::error() 吗?

完成后将 QRunnable 连接到函数/方法

QThread 不会停止/不处理信号

QT5线程关闭