首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >QCoreApplication忽略退出信号并挂起

QCoreApplication忽略退出信号并挂起
EN

Stack Overflow用户
提问于 2013-10-02 16:26:19
回答 2查看 3.9K关注 0票数 2

当在事件循环启动之前同步触发信号QCoreApplication::quit()时,信号将被忽略,应用程序将永远挂起。但是,如果是从QTimer触发,则应用程序将正确退出。在执行循环启动之前立即返回的启动任务的正确方式是什么?

下面是再现这种行为的最小代码:

hang.h

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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();
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-10-03 01:01:04

在事件循环启动之前,QCoreApplication::quit()是没有操作的,所以您不能直接调用它。quit()方法的语义实际上是:退出正在运行的事件循环。显然,如果没有运行事件循环,则不会发生任何事情。

调用app.exec()启动主线程的事件循环。在调用之前,事件循环不会运行--您的其他代码正在运行--在app.exec()主体的main()中,任何前面的代码都不会运行。因此,如果在quit()之前调用app.exec(),就没有事件循环要退出,而quit()什么也不做。

在您的代码中,一旦obj.foo()发出done()信号,app.quit()就会被调用。实际上,app.quit()方法是从信号方法的moc生成的实现中调用的。这是因为连接是直接类型的。信号只是一种机器生成的方法,它从其体内调用所有直接连接,并将QMetaCallEvent排队等待排队的连接。因此,就我们这里的目的而言,obj.foo()行等效于直接调用app.quit()。因为您是在app.exec()运行之前执行该操作的,所以它什么也不做,因为没有可以退出的事件循环。

相反,您应该将只在事件循环开始运行并使循环退出的情况下才会捕获的“东西”排队。这样做的一种方法是将事件发布到将使其退出的应用程序对象。

碰巧有一个封装时隙调用的内部QMetaCallEvent。每当QueuedConnection用于信号时隙连接时,该事件的排队由信号完成。

因此,当您的信号触发时,gui线程的事件循环的事件队列中有一个内部排队的QMetaCallEventquit()插槽不是直接调用的,只是将一个数据结构提交到事件队列。但是这种数据结构对QObject::event()有意义--当它遇到事件时,它将重新构建调用。

因此,一旦事件循环在app.exec()中开始执行,事件就会被拾取,quit()插槽被调用,应用程序将退出,因为app.exec()在运行事件循环时返回,但被告知退出它。QMetaCallEvent封装了一个函数调用。它类似于闭锁

您所需要做的就是将连接更改为排队连接。

代码语言:javascript
复制
// QT 5 syntax
connect(&obj, &hang::done, &app, &app::quit, Qt::QueuedConnection);
// QT 4 syntax
connect(obj, SIGNAL(done()), &app, SLOT(quit()), Qt::QueuedConnection);
票数 13
EN

Stack Overflow用户

发布于 2013-10-02 16:36:14

Qt文档声明,默认情况下,当发送方和接收方是同一个线程时,将直接调用它。在这种情况下,事件循环没有启动,无法对事件做出响应。解决方案是指定在QObject::connect中对事件排队

代码语言:javascript
复制
QObject::connect(&obj, SIGNAL(done()), &app, SLOT(quit()), Qt::QueuedConnection);
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19141910

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档