簡體   English   中英

QCoreApplication忽略退出信號並掛起

[英]QCoreApplication ignores quit signal and hangs

當在啟動事件循環之前同步觸發信號QCoreApplication :: quit()時,將忽略該信號並且應用程序將永久掛起。 但是,從QTimer觸發,應用程序正確退出。 在啟動exec循環之前啟動可以立即返回的任務的正確方法是什么?

以下是重現此行為的最小代碼:

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()方法的語義字面意思是: 退出正在運行的事件循環 顯然,如果沒有運行事件循環,則不會發生任何事情。

調用app.exec()啟動主線程的事件循環。 在該調用之前,事件循環不會運行 - 您的其他一些代碼正在運行 - 無論是在main()主體中的app.exec()之前。 因此,如果在app.exec() quit()之前調用quit() ,則沒有事件循環退出,並且quit()不執行任何操作。

在您的代碼中,只要obj.foo()發出done()信號, app.quit()就會被調用。 app.quit()方法實際上是從moc生成的done()信號方法實現中調用的 這是因為連接是直接類型的。 信號只是一個機器生成的方法,它從其體內調用所有直接連接,並將QMetaCallEvent排隊等待排隊連接。 因此,對於我們這里的目的, obj.foo()行等同於直接調用app.quit() 由於您在app.exec()運行之前執行此操作,因此沒有任何操作,因為沒有要退出的事件循環。

相反,你應該排隊只在事件循環開始運行時才會被拾取的“東西”, 然后讓循環退出。 執行此操作的一種方法是將事件發布到將使其退出的應用程序對象。

碰巧有一個內部QMetaCallEvent封裝了插槽調用。 每當QueuedConnection用於信號 - 時隙連接時,該事件的排隊由信號完成。

因此,當您的信號觸發時,在gui線程的事件循環的事件隊列中內部排隊有一個QMetaCallEvent 不會直接調用quit()槽,只會將數據結構發布到事件隊列中。 但是這個數據結構對QObject::event()有意義 - 它會在遇到事件時重新構建調用。

因此,一旦事件循環在app.exec()開始執行,事件就被拾取,調用quit()槽,並且應用程序退出,因為app.exec()在運行事件循環時返回,但是被告知退出它。 QMetaCallEvent封裝了一個函數調用。 它類似於封閉

您需要做的就是將連接更改為排隊的連接。

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

Qt文檔指出,默認情況下,當發送方和接收方是同一個線程時,它會直接調用。 在這種情況下,事件循環不會啟動,也無法響應事件。 解決方案是指定在QObject :: connect中對事件進行排隊

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM