QCoreApplication忽略退出信号并挂起 [英] QCoreApplication ignores quit signal and hangs

查看:2634
本文介绍了QCoreApplication忽略退出信号并挂起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当在事件循环开始之前同步触发QCoreApplication :: quit()信号时,将忽略该信号,应用程序将永久挂起。但是,从QTimer触发,应用程序正确退出。

When the signal QCoreApplication::quit() is triggered synchronously before the event loop is started, the signal is ignored and the application hang forever. However, is triggered from QTimer, the application quits correctly. What would be the proper way to start a task that can return immediately before the exec loop is started?

这是一个最小的代码来重现这种行为:

Here is a minimal code to reproduce this behavior:

hang.h

#ifndef HANG_H
#define HANG_H

#include <QObject>

class hang : public QObject
{
    Q_OBJECT
public:
    explicit hang(QObject *parent = 0);

signals:
    void done();
public slots:
    void foo();
};

#endif // HANG_H

hang.cpp

#include "hang.h"
#include <iostream>

hang::hang(QObject *parent) :
    QObject(parent)
{
}

void hang::foo()
{
    std::cout << "foo emit done()" << std::endl;
    emit done();
}

main.cpp

#include <QCoreApplication>
#include <QTimer>
#include <hang.h>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    hang obj;

    QObject::connect(&obj, SIGNAL(done()), &app, SLOT(quit()));

    // obj.foo() does emit done(), but app hang on exec
    obj.foo();

    // If done() signal is triggered from the timer, app quits correctly
    //QTimer::singleShot(0, &obj, SLOT(foo()));

    return app.exec();
}


推荐答案

QCoreApplication :: quit()是在事件循环开始之前的无操作,因此不能直接调用它。 quit()方法的语义在字面上:退出运行事件循环。显然,如果没有事件循环正在运行,则什么也不会发生。

QCoreApplication::quit() is a no-op before the event loop is started, so you can't invoke it directly. The semantics of the quit() method are, literally: Quit the running event loop. Obviously, if no event loop is running, nothing happens.

调用 app.exec()启动主线程事件循环。在调用之前,事件循环不会运行 - 您的一些其他代码正在运行 - 无论在正文中 app.exec() main()。因此,如果在 app.exec()之前调用 quit(),则没有要退出的事件循环, code> quit()什么也不做。

Invoking app.exec() starts the main thread's event loop. Before that call, the event loop doesn't run - some other code of yours is running - whatever precedes app.exec() in the body of main(). Thus, if you call quit() before app.exec(), there's no event loop to quit, and quit() does nothing.

在您的代码中,一旦 obj.foo )发出 done()信号,调用 app.quit() app.quit()方法实际上是从moc生成的 done()信号方法。这是因为连接是直接类型。一个信号只是一个机器生成的方法,从它的体内调用所有直接连接,并为队列连接排队 QMetaCallEvent 。所以,对于我们这里的目的, obj.foo()行等效于直接调用 app.quit() 。因为你在 app.exec()正在运行之前没有任何事情,因为没有事件循环退出。

In your code, as soon as obj.foo() emits the done() signal, the app.quit() gets invoked. The app.quit() method is in fact called from the moc-generated implementation of the done() signal method. This is because the connection is of a direct type. A signal is just a machine-generated method that invokes all direct connections from within its body, and queues QMetaCallEvent for queued connections. So, for our purpose here, the obj.foo() line is equivalent to directly invoking app.quit(). Since you do it before app.exec() is running, it does nothing as there's no event loop to exit.

相反,你应该排队东西,只有当事件循环开始运行,并让循环退出然后。一种方法是将一个事件发布到应用程序对象,使其退出。

Instead, you should queue "something" that will only be picked up once the event loop starts running and make the loop quit then. One way of doing it is to post an event to the application object that will make it quit.

这样发生的是有一个内部的 QMetaCallEvent ,封装了插槽调用。 QueuedConnection 用于信号槽连接时,此事件的排队由信号完成。

It so happens that there is an internal QMetaCallEvent that encapsulates slot calls. The queueing of this event is done by the signal whenever a QueuedConnection is used for the signal-slot connection.

所以,当你的信号触发,有一个 QMetaCallEvent 内部排队在gui线程的事件循环的事件队列。 quit() slot不会被直接调用,只是一个数据结构被发布到事件队列。但是,该数据结构对 QObject :: event()有意义 - 它将在遇到事件时重新构建调用。

So, when your signal fires, there's a QMetaCallEvent internally queued in the event queue of the gui thread's event loop. The quit() slot is not invoked direcly, just a data structure is posted to the event queue. But that data structure has a meaning to QObject::event() - it will reconstitute the call when it encounters the event.

所以,一旦事件循环在 app.exec()中开始执行,事件就被选中, quit code> slot被调用,并且应用程序退出,因为 app.exec()在运行事件循环时返回,但被告知退出。 QMetaCallEvent 封装一个函数调用。它类似于关闭

So, once the event loop starts executing in app.exec(), the event is picked up, the quit() slot is called, and the application is quit since app.exec() returns as it was running an event loop, but was told to quit it. The QMetaCallEvent encapsulates a function call. It is akin to a closure.

// QT 5 syntax
connect(&obj, &hang::done, &app, &app::quit, Qt::QueuedConnection);
// QT 4 syntax
connect(obj, SIGNAL(done()), &app, SLOT(quit()), Qt::QueuedConnection);

这篇关于QCoreApplication忽略退出信号并挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆