![](/img/trans.png)
[英]Error when calling quit() on a QCoreApplication within a QThread
[英]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.