关于信号:QThread finish()连接到QObject的删除之后

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 running

when 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,并在run返回之后执行它们

来源: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;
}