【问题标题】:Qt objects can still be deletedLater() without event loop?Qt 对象仍然可以在没有事件循环的情况下删除Later()?
【发布时间】:2014-05-30 13:59:56
【问题描述】:

我对 Qt 中的线程和事件循环感到困惑。

QThread 通常在 run() 中运行 exec()。但是当你覆盖run()时,就不会有事件循环了。

This (older) doc 声明在没有事件循环的线程中创建的对象上调用 deleteLater() 不起作用:

如果没有事件循环正在运行,则不会将事件传递给对象。 例如,如果您在线程中创建了一个 QTimer 对象但从不调用 exec(),QTimer 永远不会发出它的 timeout() 信号。打电话 deleteLater() 也不起作用。 (这些限制适用于主要 线程也是如此。)

不过,看看下面的代码:

class MyObject : public QObject
{
    Q_OBJECT

    QString content;

public:
    MyObject(QObject *parent = 0);
    ~MyObject();
};

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = 0);
    void run();

signals:

public slots:

};

MyObject::MyObject(QObject *parent) :
    QObject(parent),
    content("foobar")
{}

MyObject::~MyObject()
{
    // This code is still executed before I close the program. How?
    qDebug() << "Destroying MyObject";
}

MyThread::MyThread(QObject *parent) :
    QThread(parent)
{}

void MyThread::run()
{
    // Creating a heap object in a thread that does not have 
    // an event loop (because I reimplemented run()).
    MyObject * objectification = new MyObject();
    sleep(1);
    objectification->deleteLater();
}

那么为什么deletelater() 调用仍然会发布一个被拾取的事件?

【问题讨论】:

    标签: c++ qt


    【解决方案1】:

    作为Qt docs state for deleteLater:-

    从 Qt 4.8 开始,如果在没有运行事件循环的线程中对一个对象调用 deleteLater(),则该对象将在线程结束时被销毁。

    当不存在事件循环时,对象仍在被删除。如果您查看 QObject::deleteLater 的 source code,您会看到发布了一个事件:-

    void QObject::deleteLater()
    {
        QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
    }
    

    那么,让我们看看when a thread is deleted 会发生什么。 QThreadData 的析构函数包括:-

    for (int i = 0; i < postEventList.size(); ++i) {
        const QPostEvent &pe = postEventList.at(i);
        if (pe.event) {
            --pe.receiver->d_func()->postedEvents;
            pe.event->posted = false;
            delete pe.event;
        }
    }
    

    如我们所见,虽然没有事件循环,但事件列表仍然可用。

    如果我们更仔细地研究QThreadPrivate(仅以一个平台为例,在本例中为 unix),您会发现当线程完成时,它会转发所有延迟删除的消息,因此它们可以继续已处理:

    QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
    

    【讨论】:

    • 我还注意到,如果您调用进行信号/槽处理的代码,即使没有事件循环,它仍然可以工作。例如,如果您在上述线程中创建一个QNetworkManager 并发出请求等,并让线程等待它完成,则所有插槽都会得到处理。我预计他们不会。 (我知道这不是你应该使用线程的方式,因为它是线程中的同步编程。这只是一个例子。)
    • 你在哪里创建 QNetworkManager?是否在继承的QThread的构造函数中,是否有父对象?
    • 会在void run()中创建,而parent是在void run()中创建的另一个对象。
    • 发送信号时,如果接收对象的线程亲和性与发送事件的地方相同,则使用直接连接,无需发布事件。调用是即时的,不需要事件循环。查看连接类型:qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-08
    • 1970-01-01
    • 2018-06-03
    • 1970-01-01
    • 1970-01-01
    • 2013-06-08
    • 1970-01-01
    相关资源
    最近更新 更多