为什么 QTimer::singleShot 在时间 1 而不是 0 时阻塞了我的主线程
Why does QTimer::singleShot block my main thread at time 1 but not 0
我一直在调试一些代码,这些代码似乎在 GUI 不可见时锁定了主线程。我已将其剥离回以下代码片段,并且我发现我的 QTimer::singleShot.
实现存在问题
为了重现这个问题,我将计时器设置为 1(毫秒)启动应用程序循环并将应用程序 window 发送到后台。最终,应用程序将逐渐停止,直到 UI 进入前台。
如果我将计时器设置为 0,那么无论主应用程序 window 位于何处,它都会完美运行,我已经将循环大小增加到 400 万次没有问题。
我想我被卡住的是,
之间有什么区别
QTimer::singleShot(1, this, SLOT(emptyListOfAnotherObjects()));
和
QTimer::singleShot(0, this, SLOT(emptyListOfAnotherObjects()));
?
为什么一个会阻塞主线程而不是另一个?
对于它的价值 运行 这个循环在不同的线程中,保持 GUI 分开,导致同样的问题。
class AnotherObject : public QObject
{
Q_OBJECT
public:
AnotherObject();
void process();
signals:
void done();
};
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void start(bool);
private slots:
void emptyListOfAnotherObjects();
void delayEmptyOfAnotherObject();
private:
QList<AnotherObject*> m_listOfAnotherObject;
Ui::MainWindow *ui;
};
==
AnotherObject::AnotherObject()
{
qDebug() << "CTOR AnotherObject" << this;
}
void AnotherObject::process()
{
emit done();
deleteLater();
}
//==
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->start, SIGNAL(clicked(bool)), this, SLOT(start(bool)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::start(bool)
{
for(long i=0; i<40000; i++){
m_listOfAnotherObject.append(new AnotherObject());
}
emptyListOfAnotherObjects();
}
void MainWindow::emptyListOfAnotherObjects()
{
if(m_listOfAnotherObject.isEmpty()) {
qDebug() << "List empty, done.";
return;
}
AnotherObject* i = m_listOfAnotherObject.takeFirst();
connect(i, SIGNAL(done()), this, SLOT(delayEmptyOfAnotherObject()));
i->process();
qDebug() << m_listOfAnotherObject.count();
}
void MainWindow::delayEmptyOfAnotherObject()
{
QTimer::singleShot(1, this, SLOT(emptyListOfAnotherObjects()));
}
谢谢
更新:
抱歉,应该补充说这是 OSX 上的 Qt 5.2。
App Nap 可能是导致此问题的原因。
我怀疑使用 invokeMethod 会在线程的事件队列中放置一条消息,这会阻止应用休眠。相反,使用 QTimer 等待并检查超时,因此一个空的事件队列将允许它进入小睡状态。
无论如何,可以从您的应用程序中禁用应用程序小睡,如所述here。
当您使用 Qt 时,您需要使用 .mm 文件 encapsulate the objective-c code。
我一直在调试一些代码,这些代码似乎在 GUI 不可见时锁定了主线程。我已将其剥离回以下代码片段,并且我发现我的 QTimer::singleShot.
实现存在问题为了重现这个问题,我将计时器设置为 1(毫秒)启动应用程序循环并将应用程序 window 发送到后台。最终,应用程序将逐渐停止,直到 UI 进入前台。
如果我将计时器设置为 0,那么无论主应用程序 window 位于何处,它都会完美运行,我已经将循环大小增加到 400 万次没有问题。
我想我被卡住的是,
之间有什么区别QTimer::singleShot(1, this, SLOT(emptyListOfAnotherObjects()));
和
QTimer::singleShot(0, this, SLOT(emptyListOfAnotherObjects()));
?
为什么一个会阻塞主线程而不是另一个?
对于它的价值 运行 这个循环在不同的线程中,保持 GUI 分开,导致同样的问题。
class AnotherObject : public QObject
{
Q_OBJECT
public:
AnotherObject();
void process();
signals:
void done();
};
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void start(bool);
private slots:
void emptyListOfAnotherObjects();
void delayEmptyOfAnotherObject();
private:
QList<AnotherObject*> m_listOfAnotherObject;
Ui::MainWindow *ui;
};
==
AnotherObject::AnotherObject()
{
qDebug() << "CTOR AnotherObject" << this;
}
void AnotherObject::process()
{
emit done();
deleteLater();
}
//==
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->start, SIGNAL(clicked(bool)), this, SLOT(start(bool)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::start(bool)
{
for(long i=0; i<40000; i++){
m_listOfAnotherObject.append(new AnotherObject());
}
emptyListOfAnotherObjects();
}
void MainWindow::emptyListOfAnotherObjects()
{
if(m_listOfAnotherObject.isEmpty()) {
qDebug() << "List empty, done.";
return;
}
AnotherObject* i = m_listOfAnotherObject.takeFirst();
connect(i, SIGNAL(done()), this, SLOT(delayEmptyOfAnotherObject()));
i->process();
qDebug() << m_listOfAnotherObject.count();
}
void MainWindow::delayEmptyOfAnotherObject()
{
QTimer::singleShot(1, this, SLOT(emptyListOfAnotherObjects()));
}
谢谢
更新:
抱歉,应该补充说这是 OSX 上的 Qt 5.2。
App Nap 可能是导致此问题的原因。
我怀疑使用 invokeMethod 会在线程的事件队列中放置一条消息,这会阻止应用休眠。相反,使用 QTimer 等待并检查超时,因此一个空的事件队列将允许它进入小睡状态。
无论如何,可以从您的应用程序中禁用应用程序小睡,如所述here。
当您使用 Qt 时,您需要使用 .mm 文件 encapsulate the objective-c code。