完成()连接到QObject的deletelater的QThread

时间:2012-08-24 18:12:37

标签: signals qthread qobject quit

在这里提出这个问题之前,我已经想了很多并阅读了很多文章。这些文章都没有给我一个正确的答案。

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对象具有新线程的亲和力。

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

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

  

**安排此对象删除。   当控制返回到事件循环时,将删除该对象。如果事件循环是>没有跑

     

调用此函数时(例如,在调用deleteLater()时)   在QCoreApplication :: exec()之前的对象,该对象将被删除   一旦事件循环开始。

     

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

     

注意:它   可以安全地多次调用此函数;当第一个推迟   删除事件已传递,该对象的任何待处理事件都是   从事件队列中删除。**

因此,当没有eventloop时,因为线程已经退出并且它已经提出了已完成的信号,我们将不再再次启动相同的线程。在这种情况下,将永远不会处理deleteLater(),因为事件循环不存在,并且根本不会删除worker对象。这不会造成内存泄漏吗?

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

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

上面提到的文章链接中有很多评论。甚至作者也无法完全回答这个问题

2 个答案:

答案 0 :(得分:3)

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

换句话说,QThread将收集所有挂起的deleteLaters并在run返回后执行它们

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

答案 1 :(得分: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;
}