gpt4 book ai didi

c++ - 是什么导致同步块(synchronized block)函数 `sleep` 在 GUI 和非 GUI 程序中的不同结果

转载 作者:行者123 更新时间:2023-11-28 01:32:12 25 4
gpt4 key购买 nike

我使用同步块(synchronized block)QThread::sleep()函数进行计时,计时显示一个接一个的数字。

预期的运行进程是同步阻塞当前线程 2 秒,然后运行以下代码来更改显示的数字,并同步阻塞另外 2 秒,依此类推...,这种想法在非 GUI 程序中运行良好。

但在 GUI 模式下,标签只显示 9,这是最后要显示的数字。

是什么导致了 GUI 和非 GUI 程序中同步阻塞函数 sleep 的不同结果?

#include <windows.h>
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QThread>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}

Widget::~Widget()
{
delete ui;
}

//slot of start timing button
void Widget::on_pushButton_2_clicked()
{
for(int i=0;i<10;i++){
QThread.sleep(2);
ui->label->setText(QString::number(i));
}
}

最佳答案

GUI 需要不断验证鼠标、键盘等事件,并在满足某些条件时执行操作,这称为事件循环。在sleep()的情况下是一个阻塞任务,不允许eventloop运行,生成GUI卡住(如果你想验证它,尝试改变窗口的大小) ,所以在 GUI 线程中你应该避免使用那种函数,你必须将它们变成异步或在另一个线程中执行的阻塞任务。

但是 sleep() 的任务可以在不阻塞 GUI 的情况下用 QTimer 代替:

*.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QTimer>
#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = 0);
~Widget();

private slots:
void on_pushButton_2_clicked();
void onTimeout();
private:
Ui::Widget *ui;
int counter;
QTimer timer;
};

#endif // WIDGET_H

*.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QLabel>

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
counter = 0;
connect(&timer, &QTimer::timeout, this, &Widget::onTimeout);
}

Widget::~Widget()
{
delete ui;
}

void Widget::on_pushButton_2_clicked()
{
timer.start(2000);
}

void Widget::onTimeout()
{
ui->label->setText(QString::number(counter));
counter++;
if(counter > 10){
counter = 0;
timer.stop();
}
}

另一种选择是将QEventLoopQTimer一起使用:

void Widget::on_pushButton_2_clicked()
{
for(int i=0;i<10;i++){
QEventLoop loop;
QTimer::singleShot(2000, &loop, &QEventLoop::quit);
loop.exec();
ui->label->setText(QString::number(i));
}
}

更新:

是什么导致同步阻塞函数sleep在GUI程序和非GUI程序中的结果不同?

如果您使用 Qt 创建非 GUI 应用程序,您也会遇到问题,尽管效果可能不太明显。

在我所说的 GUI 的情况下,有一个处理事件的事件循环,其中包括重新绘制的事件循环,我的意思是当您在 QLabel 中设置新文本时,这不会被绘制自动但 Qt 决定正确的时刻。这就是为什么当您使用 QThread::sleep() 时您没有时间更新绘画。

显然,在非 GUI 应用程序中,事件循环不会验证许多事件,因为它绘制的事件看不到效果,事实上,在仅打印数字的脚本中,它不会验证任何事件。

为了注意到这个问题,让我们使用下面的例子:

#include <QCoreApplication>
#include <QThread>
#include <QTimer>

#include <QDebug>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [](){
qDebug()<< "hello world";
});

timer.start(1000);

qDebug()<< "start blocking";
QThread::sleep(10);
qDebug()<< "end blocking";

return a.exec();
}

我们将看到在 sleep() 结束之前不会打印任何内容,也就是说,阻止允许 QTimer 完成其工作的事件循环。

回复您的评论:

  • 但还是让我不解的是为什么执行完sleep函数后,停止阻塞当前线程后,下面的代码不能正常运行

  • ui->label->setText(QString::number(i)); 这个语句,就在 sleep 函数之后

    <

绘画等异步任务的优先级低于同步任务,也就是说Qt会先执行for循环,然后才做异步任务,所以在存放QLabel的for变量中> 文本被更新,也就是说 0, 1 , ..., 9,然后任务被移交给 eventloop 以便它只绘制最后一个值,即 9。

注意:

您可以使用 QXXXApplication::processEvents() 强制 evenloop 在同步执行中更新,但这通常被认为是一种不好的做法,我只是展示它以便您了解它但避免使用它:

for(int i=0;i<10;i++){
QThread.sleep(2);
ui->label->setText(QString::number(i));
QApplication::processEvents();
}

关于c++ - 是什么导致同步块(synchronized block)函数 `sleep` 在 GUI 和非 GUI 程序中的不同结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51044697/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com