QThread finished() connected to deletelater of a QObject
在问这个问题之前,我已经想了很多,读了很多文章。这些文章都没有给我适当的答案。
How To Really, Truly Use QThreads; The Full Explanation
1 2 3 4 5 6 7 8 9 | 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(); |
辅助对象具有新线程的亲和力。
1> Worker结束信号将在线程上调用quit()。这将结束线程的事件循环并启动线程完成信号。
2>工作程序完成信号连接到工作程序deleteLater()。根据deleteLater()文档
**Schedules this object for deletion.
The object will be deleted when control returns to the event loop. If the event loop is > not runningwhen this function is called (e.g. deleteLater() is called on an
object before QCoreApplication::exec()), the object will be deleted
once the event loop is started.Note that entering and leaving a new
event loop (e.g., by opening a modal dialog) will not perform the
deferred deletion; for the object to be deleted, the control must
return to the event loop from which deleteLater() was called.Note: It
is safe to call this function more than once; when the first deferred
deletion event is delivered, any pending events for the object are
removed from the event queue.**
因此,当没有事件循环时,由于线程已经退出并且已经发出完成信号,因此我们将不再再次启动同一线程。在这种情况下,将永远不会处理deleteLater(),因为事件循环不存在,并且完全不会删除worker对象。这不会造成内存泄漏吗?
1 2 | connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(worker, SIGNAL(finished()), thread, SLOT(quit())); |
如果我们认为交换两条线可以解决问题,那么我还有另一个问题。 QT明确指出,信号发射时插槽被调用的顺序不确定
在上面提到的文章链接中有很多评论。甚至作者也无法完全回答问题
QThread将在发送完成信号后执行QCoreApplication :: sendPostedEvents事件类型为QEvent :: DeferredDelete
换句话说,QThread将收集所有未决的deleteLater,并在
来源:https://qt.gitorious.org/qt/qtbase/source/c657bb7b51115d6e1719166fb502bb0ca1dcf4e8:src/corelib/thread/qthread_win.cpp#L363-462
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | //! 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; } |