Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/160.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ QtConcurrent-在发布到UI线程的数千个结果中保持GUI响应_C++_Multithreading_Qt_Qtconcurrent_Qfuture - Fatal编程技术网

C++ QtConcurrent-在发布到UI线程的数千个结果中保持GUI响应

C++ QtConcurrent-在发布到UI线程的数千个结果中保持GUI响应,c++,multithreading,qt,qtconcurrent,qfuture,C++,Multithreading,Qt,Qtconcurrent,Qfuture,我有一个应用程序,它可能有长时间运行的任务,也可能有数千或数百万个任务或结果 这个特定的应用程序(下面的代码)没有任何价值,但它的目的是提供一个通用用例,说明需要在“数千”个结果中维护一个响应良好的UI 要明确的是,我知道应该减少用户界面的轮询次数。我的问题是关于可以应用于此(和其他类似)场景以保持响应性UI的设计原则 我的第一个想法是使用a,每200毫秒处理一次所有“结果”,这是一个可以找到但需要改进的例子 哪些方法可用,哪些方法更适合保持响应性用户界面? 下面是我试图解释的一个简单例子。我

我有一个应用程序,它可能有长时间运行的任务,也可能有数千或数百万个任务或结果

这个特定的应用程序(下面的代码)没有任何价值,但它的目的是提供一个通用用例,说明需要在“数千”个结果中维护一个响应良好的UI

要明确的是,我知道应该减少用户界面的轮询次数。我的问题是关于可以应用于此(和其他类似)场景以保持响应性UI的设计原则

我的第一个想法是使用a,每200毫秒处理一次所有“结果”,这是一个可以找到但需要改进的例子

哪些方法可用,哪些方法更适合保持响应性用户界面?


下面是我试图解释的一个简单例子。我有一个用户界面:

  • 生成一个整数列表

  • 将其传递到映射函数中,以pow(x,2)值,然后

  • 衡量进展

  • 运行此应用程序时,单击“开始”按钮将运行该应用程序,但由于用户处理结果的频率:,用户界面无法响应任何用户单击,因此尝试“暂停”或“停止”(取消)是徒劳的

    包装器用于
    QtConcurrent::mapped()
    传递lambda的函数(用于成员函数)

    此连接侦听来自未来的结果,并对其进行操作(此连接函数在UI线程上运行)。因为我正在模拟大量的“ui更新”,显示为
    int entries=50000000
    ,每次处理结果时,都会调用
    QFutureWatcher::resultradayat


    运行+/-2秒时,UI不会对链接到线程1暂停上的
    和线程1停止上的
    的“暂停”或“停止”单击作出响应。分别单击

    使用
    QtConcurrent::mapped
    的方法非常合理,我认为从理论上讲,这可能是解决这个问题的一个好办法。这里的问题是,添加到事件队列的事件数量太多,无法使UI保持响应

    UI没有响应的原因是GUI线程中只有一个事件队列。因此,您单击的按钮
    事件将与
    resultradayt
    事件一起排队。但是队列只是一个队列,所以如果您的按钮事件在30'000'000的ResulteAYAT事件之后进入队列,那么只有轮到它时才会处理它。
    resize
    move
    事件也是如此。因此,UI感觉迟钝,没有响应能力

    一种可能是修改映射函数,以代替单个数据点接收数据块。例如,我将50000数据拆分为1000批50000数据。您可以看到,在这种情况下,UI在所有执行过程中都是响应的。我还在每个函数中添加了20ms延迟,否则执行速度太快,我甚至无法按下停止/暂停按钮

    您的代码中还有几个小注释:

    • 原则上,您不需要包装器类,因为您可以直接传递成员函数(再次参见下面的第一个示例)。如果您有问题,可能与您正在使用的Qt版本或编译器有关
    • 实际上,您正在更改传递给
      doubleValue
      的值。这实际上使得从函数返回值变得无用
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    类Widget:publicqwidget{
    Q_对象
    公众:
    结构IntStream{
    int值;
    };
    小部件(QWidget*parent=nullptr);
    静态QVector双值(常数QVector&v);
    公众时段:
    void startThread();
    void pauseResumeThread();
    void stop thread();
    私人:
    静态constexpr int批处理大小{50000};
    静态constexpr int总计{1000};
    QFutureWatcher m_futureWatcher;
    未来,未来;
    QProgressBar m_progressBar;
    QVector m_intList;
    int m_计数{0};
    };
    Widget::Widget(QWidget*parent):QWidget(parent)
    {
    自动布局{new QVBoxLayout{};
    自动按钮_startThread{new QPushButton{“Start Thread”};
    布局->添加小部件(按钮\u开始线程);
    连接(按钮启动线程,&QPushButton::单击,
    这是(Widget::startThread)(&Widget::startThread);
    自动按钮_pauseResumeThread{new QPushButton{“暂停/恢复线程”};
    布局->添加小部件(按钮_pauseResumeThread);
    连接(按钮\u暂停刷新,&QPushButton::单击,
    这是一个小部件(&Widget::pauseResumeThread);
    自动按钮{u停止线程{new QPushButton{“停止线程”};
    布局->添加小部件(按钮\停止线程);
    连接(按钮\停止线程,&QPushButton::单击,
    这是&Widget::stopThread);
    布局->添加小部件(&m_progressBar);
    设置布局(布局);
    qDebug()生成())});
    m_intList.append(v);
    }
    }
    QVector小部件::doubleValue(constqvector&v)
    {
    QThread::msleep(20);
    矢量输出;
    用于(常数自动和x:v){
    append(IntStream{x.value*x.value});
    }
    返回;
    }
    void小部件::startThread()
    {
    if(m_future.isRunning())
    返回;
    
    qDebug()也许你可以创建一个专门的结果处理线程,并将数百万个结果导入该线程。它可以对结果进行任何初始处理,并将结果汇总成“执行摘要”它将以更长的时间间隔移交给GUI线程。感谢您的回答-我认为对线程进行子类化或添加延迟(我不想这么做)都会有帮助,但添加X ms延迟将增加nX ms延迟
    #include <functional>
    
    template <typename ResultType>
    class MappedFutureWrapper
    {
    public:
        using result_type = ResultType;
    
        MappedFutureWrapper<ResultType>(){}
        MappedFutureWrapper<ResultType>(std::function<ResultType (ResultType)> function): function(function){ }
        MappedFutureWrapper& operator =(const MappedFutureWrapper &wrapper) {
            function = wrapper.function;
            return *this;
        }
        ResultType operator()(ResultType i) {
            return function(i);
        }
    
    private:
        std::function<ResultType(ResultType)> function;
    };
    
    class MainWindow : public QMainWindow {
         Q_OBJECT
    
      public:
         struct IntStream {
             int value;
         };
    
         MappedFutureWrapper<IntStream> wrapper;
         QVector<IntStream> intList;
    
         int count = 0;
         int entries = 50000000;
    
         MainWindow(QWidget* parent = nullptr);
         static IntStream doubleValue(IntStream &i);
         ~MainWindow();
    
        private:
           Ui::MainWindow* ui;
           QFutureWatcher<IntStream> futureWatcher;
           QFuture<IntStream> future;
    
           //...
    }
    
    MainWindow::MainWindow(QWidget* parent)
         : QMainWindow(parent)
         , ui(new Ui::MainWindow)
    {
         ui->setupUi(this);
        qDebug() << "Launching";
    
         intList = QVector<IntStream>();
         for (int i = 0; i < entries; i++) {
             int localQrand = qrand();
             IntStream s;
             s.value = localQrand;
             intList.append(s);
         }
    
         ui->progressBar->setValue(0);
    
    }
    
    MainWindow::IntStream MainWindow::doubleValue(MainWindow::IntStream &i)
    {
        i.value *= i.value;
        return i;
    }
    
    void MainWindow::on_thread1Start_clicked()
    {
        qDebug() << "Starting";
    
        // Create wrapper with member function
        wrapper = MappedFutureWrapper<IntStream>([this](IntStream i){
            return this->doubleValue(i);
        });
    
        // Process 'result', need to acquire manually
        connect(&futureWatcher, &QFutureWatcher<IntStream>::resultReadyAt, [this](int index){
            auto p = ((++count * 1.0) / entries * 1.0) * 100;
            int progress = static_cast<int>(p);
            if(this->ui->progressBar->value() != progress) {
                qDebug() << "Progress = " << progress;
                this->ui->progressBar->setValue(progress);
            }
        });
    
        // On future finished
        connect(&futureWatcher, &QFutureWatcher<IntStream>::finished, this, [](){
            qDebug() << "done";
        });
    
        // Start mapped function
        future = QtConcurrent::mapped(intList, wrapper);
        futureWatcher.setFuture(future);
    }
    
    void MainWindow::on_thread1PauseResume_clicked()
    {
        future.togglePaused();
        if(future.isPaused()) {
            qDebug() << "Paused";
        } else  {
            qDebug() << "Running";
        }
    }
    
    void MainWindow::on_thread1Stop_clicked()
    {
        future.cancel();
        qDebug() << "Canceled";
    
        if(future.isFinished()){
            qDebug() << "Finished";
        } else {
            qDebug() << "Not finished";
        }
    
    }
    
    MainWindow::~MainWindow()
    {
         delete ui;
    }
    
    connect(&futureWatcher, &QFutureWatcher<IntStream>::resultReadyAt, [this](int index){
        auto p = ((++count * 1.0) / entries * 1.0) * 100;
        int progress = static_cast<int>(p);
        if(this->ui->progressBar->value() != progress) {
            qDebug() << "Progress = " << progress;
            this->ui->progressBar->setValue(progress);
        }
    });
    
    #include <QApplication>
    #include <QMainWindow>
    #include <QProgressBar>
    #include <QPushButton>
    #include <QRandomGenerator>
    #include <QtConcurrent>
    #include <QVBoxLayout>
    
    
    class Widget : public QWidget {
        Q_OBJECT
    
    public:
        struct IntStream {
            int value;
        };
    
        Widget(QWidget* parent = nullptr);
        static QVector<IntStream> doubleValue(const QVector<IntStream>& v);
    
    public slots:
        void startThread();
        void pauseResumeThread();
        void stopThread();
    
    private:
        static constexpr int                BATCH_SIZE {50000};
        static constexpr int                TOTAL_BATCHES {1000};
        QFutureWatcher<QVector<IntStream>>  m_futureWatcher;
        QFuture<QVector<IntStream>>         m_future;
        QProgressBar                        m_progressBar;
        QVector<QVector<IntStream>>         m_intList;
        int                                 m_count {0};
    };
    
    
    Widget::Widget(QWidget* parent) : QWidget(parent)
    {
        auto layout {new QVBoxLayout {}};
    
        auto pushButton_startThread {new QPushButton {"Start Thread"}};
        layout->addWidget(pushButton_startThread);
        connect(pushButton_startThread, &QPushButton::clicked,
                this, &Widget::startThread);
    
        auto pushButton_pauseResumeThread {new QPushButton {"Pause/Resume Thread"}};
        layout->addWidget(pushButton_pauseResumeThread);
        connect(pushButton_pauseResumeThread, &QPushButton::clicked,
                this, &Widget::pauseResumeThread);
    
        auto pushButton_stopThread {new QPushButton {"Stop Thread"}};
        layout->addWidget(pushButton_stopThread);
        connect(pushButton_stopThread, &QPushButton::clicked,
                this, &Widget::stopThread);
    
        layout->addWidget(&m_progressBar);
    
        setLayout(layout);
    
        qDebug() << "Launching";
    
        for (auto i {0}; i < TOTAL_BATCHES; i++) {
            QVector<IntStream> v;
            for (auto j {0}; j < BATCH_SIZE; ++j)
                v.append(IntStream {static_cast<int>(QRandomGenerator::global()->generate())});
            m_intList.append(v);
        }
    }
    
    QVector<Widget::IntStream> Widget::doubleValue(const QVector<IntStream>& v)
    {
        QThread::msleep(20);
        QVector<IntStream> out;
        for (const auto& x: v) {
            out.append(IntStream {x.value * x.value});
        }
        return out;
    }
    
    void Widget::startThread()
    {
        if (m_future.isRunning())
            return;
        qDebug() << "Starting";
    
        m_count = 0;
    
        connect(&m_futureWatcher, &QFutureWatcher<IntStream>::resultReadyAt, [=](int){
            auto progress {static_cast<int>(++m_count * 100.0 / TOTAL_BATCHES)};
            if (m_progressBar.value() != progress && progress <= m_progressBar.maximum()) {
                m_progressBar.setValue(progress);
            }
        });
    
        connect(&m_futureWatcher, &QFutureWatcher<IntStream>::finished,
                [](){
                    qDebug() << "Done";
                });
    
        m_future = QtConcurrent::mapped(m_intList, &Widget::doubleValue);
        m_futureWatcher.setFuture(m_future);
    }
    
    void Widget::pauseResumeThread()
    {
        m_future.togglePaused();
    
        if (m_future.isPaused())
            qDebug() << "Paused";
        else
            qDebug() << "Running";
    }
    
    void Widget::stopThread()
    {
        m_future.cancel();
        qDebug() << "Canceled";
    
        if (m_future.isFinished())
            qDebug() << "Finished";
        else
            qDebug() << "Not finished";
    }
    
    
    int main(int argc, char* argv[])
    {
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    }
    
    #include "main.moc"