что приводит к разным результатам синхронно блочной функции `sleep` в GUI и не GUI программах - PullRequest
0 голосов
/ 26 июня 2018

Я использую синхронно блокировать функцию QThread::sleep() для определения времени, которое показывает номер один за другим по времени.

ожидаемый запущенный процесс - это синхронная блокировка текущего потока на 2 секунды и выполнение следующего кода для изменения отображаемого числа, а также синхронная блокировка еще на 2 секунды и т. Д., Эта мысль хорошо работает в программах без графического интерфейса пользователя.

, но в режиме графического интерфейса на этикетке отображается только 9, что является последним отображаемым числом.

что приводит к различному результату функции синхронной блокировки 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));
    }
}

1 Ответ

0 голосов
/ 26 июня 2018

Графический интерфейс должен постоянно проверять такие события, как мышь, клавиатура и т. Д., И выполнять действия, если выполняются определенные условия, что называется eventloop. В случае sleep() это задача блокировки, которая не позволяет запускать eventloop, генерируя GUI для остановки (если вы хотите это проверить, попробуйте изменить размер окна), поэтому внутри потока GUI вы Следует избегать использования такого рода функций, при блокировании задач вы должны превратить их в асинхронные или выполнить в другом потоке.

Но задание sleep() можно заменить QTimer без блокировки графического интерфейса:

*. Ч

#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

*. Каст

#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();
    }
}

Другой вариант - использовать QEventLoop с QTimer:

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));
    }
}

Обновление:

что приводит к различному результату спящего режима синхронной блокировки в графическом интерфейсе и в программе без графического интерфейса?

Если вы создаете приложение без графического интерфейса с использованием Qt, у вас также будут проблемы, хотя эффект может быть менее заметен.

В случае с графическим интерфейсом, как я уже сказал, есть цикл событий, который обрабатывает события, и среди них перерисовка, я имею в виду, когда вы устанавливаете новый текст в QLabel, он не рисуется автоматически, но Qt решает подходящий момент Вот почему при использовании QThread::sleep() у вас нет времени на обновление картины.

Очевидно, что в приложениях без графического интерфейса eventloop не проверяет много событий, поскольку нарисованное им не видит эффекта, фактически в сценарии, который печатает только числа, не проверяет никаких событий.

Чтобы заметить проблему, давайте использовать следующий пример:

#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 выполнять свою работу.

Отвечая на ваш комментарий:

  • но меня все еще удивляет, почему после завершения выполнения функции сна, после прекращения блокировки текущего потока, следующие коды не могут работать как обычно

  • ui->label->setText(QString::number(i)); это утверждение, сразу после функции сна

Асинхронные задачи, такие как рисование, имеют меньший приоритет, чем синхронные задачи, то есть сначала Qt выполнит цикл for, а затем просто выполнит асинхронные задачи, поэтому в переменной for, которая хранит текст QLabel, обновляется, то есть 0, 1, ..., 9, и после этого задача передается в цикл событий, так что он просто рисует последнее значение, то есть 9.

Примечание:

Вы можете принудительно обновить Evenloop в синхронном исполнении с помощью QXXXApplication::processEvents(), но это часто считается плохой практикой, я только показываю это, чтобы вы знали это, но избегали его использования:

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