- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个应用程序,它可能会长时间运行任务,也可能有成千上万个结果。
这个特定的应用程序(下面的代码)没有任何值(value),但是它旨在提供一个通用的用例,即在成千上万的结果中保持响应式UI的需求。
需要明确的是,我知道应该减少对UI进行轮询的次数。我的问题是关于可用于保持响应式UI的设计原理(可应用于此(和其他类似)方案)。
我的第一个想法是使用QTimer并在例如200ms,一个可以在here中找到但需要注意的示例。
哪些方法可用,哪些方法可以保持响应式UI?
我尝试解释的一个简单示例如下。我有一个UI:
QtConcurrent::mapped()
函数的包装器(用于成员函数)
#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;
}
on_thread1Start_clicked()
方法时,除了添加以下连接之外,它还启动了将来的操作:
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);
}
});
int entries = 50000000;
所示的大量“ui更新”,因此每次处理结果时,都会调用
QFutureWatcher<IntStream>::resultReadyAt
。
on_thread1PauseResume_clicked()
和
on_thread1Stop_clicked
的“暂停”或“停止”点击。
最佳答案
您使用QtConcurrent::mapped
的方法非常合理,我认为从理论上讲,这可能是解决此类问题的好方法。这里的问题是,添加到事件队列中的事件数量太多,无法保持UI的响应速度。
UI不响应的原因是,GUI线程中只有一个事件队列。结果,您的按钮clicked
事件与resultReadyAt
事件一起排队。但是队列只是一个队列,因此,如果您的按钮事件在resultReadyAt事件说30'000'000之后进入队列,则仅在轮到该事件时才对其进行处理。 resize
和move
事件也是如此。结果,UI感觉迟钝并且没有响应。
一种可能是修改您的映射功能,以便接收单个数据块而不是单个数据点。例如,我将50000数据拆分为1000批次的50000数据。您可以看到,在这种情况下,UI在所有执行过程中都是响应式的。我还在每个函数中添加了20ms的延迟,否则执行速度如此之快,以至于我什至无法按下停止/暂停按钮。
您的代码也有一些小注释:
doubleValue
的值。实际上,这使从函数返回值变得毫无用处。 #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"
关于c++ - QtConcurrent-在数千个发布到UI线程的结果中保持GUI响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62144289/
Qt 文档说明了以下关于 QtConcurrent::blockingMap 的内容: Note: This function will block until all items in the se
我有一个应用程序,它可能会长时间运行任务,也可能有成千上万个结果。 这个特定的应用程序(下面的代码)没有任何值(value),但是它旨在提供一个通用的用例,即在成千上万的结果中保持响应式UI的需求。
我有一个递归目录复制功能,我想在后台运行。该函数有两个 QString 参数,文件路径和目录。 来自.pro: QT += core gui sql network concurrent greate
我已经设计了一种算法,现在我正在研究实现以在多核上解决它。基本上我给每个核心相同的问题,我会选择得分最高的解决方案。但是,我注意到使用多核会减慢代码的运行时间,但我不明白为什么。所以我创建了一个非常简
在 Qt 4.7 Reference for QThreadPool 中,我们发现: void QThreadPool::releaseThread() Releases a thread previ
我在我的应用程序中构建了一个基于 QFuture 的异步网络外观。大致是这样工作的: namespace NetworkFacade { QByteArray syncGet(const QU
我正在用 QT 做多线程程序。 我使用此代码来尝试它是否按我预期的那样工作。 QFuture t1 = QtConcurrent::run(thread_process1, (void *)this)
我在项目列表上运行 QTConcurrent::Map 以执行一些图像处理任务。这在只有几个线程的机器上运行良好,但由于我的处理代码的内存需求,在具有大量线程的机器上会遇到问题。 是否可以为 QTCo
我想用 QtConcurrent::map 运行一个函数,但总是出错......我在主窗口中有两个函数:on_listWidget_itemClicked 和 _fillTreeWithList(QS
我想在 Qt 中从我使用 QtConcurrent::run 调用的函数发出信号 这可能吗?看来我的槽位永远不会被调用。所有信号、槽和函数都是同一类对象的一部分。我尝试在主线程和从线程中建立连接。我并
我目前正在学习用于多线程应用程序的QtConncurrenct。因此,出于测试目的,我决定实现一个简单的程序,对列表中的整数求和,代码如下: #include #include #includ
你好,我正在尝试将 QList 传递给 QtConcurrent::Map 函数,但它无法启动,我不明白为什么,也许有人知道可能是什么问题? 这是类方法代码 void MainWindow::find
如何将 QtConcurrent::mapped 与成员函数一起用作运算符?目前我正在使用一个可调用对象(这是一个丑陋的解决方案): struct Vectorizer { Vectorize
这个问题在这里已经有了答案: is it possible to use QtConcurrent::run() with a function member of a class (2 个答案)
我使用 QtConcurrence 在单独的线程中运行一个函数,但我想停止、暂停或终止该线程,但我做不到。我读了这个: Note that the QFuture returned by QtConc
我在网上找到了一些引用文献,其中人们说这行不通,但他们从未解释过为什么这行不通。 在伪代码中我正在做这样的事情: void MyObject::doWork() { QList worklis
我有一个用于将文件压缩成不同格式的类。我正在尝试使用 QtConcurrent 在后台运行压缩。有了这个,我有两个功能: 将文件路径作为字符串和压缩格式 获取文件路径和压缩格式的 vector 问题是
我正在尝试将一个成员函数传递给 QtConcurrent::run() 我试过这样做: GDALDriver *poNITFDriver; future = QtConcurrent::run(poN
我试图在另一个线程中运行非静态成员函数。如果我去: void *(PortManager::*innerAskPtr)() = &this->innerAsk; QFuture f = QtConcu
我正在尝试实现预加载与另一个线程中请求的图像相邻的图像,并将它们存储在 QPixmapCache 中,但即使我不存储图像,似乎也存在内存泄漏。 Valgrind 显示使用此方法没有内存泄漏,但程序的内
我是一名优秀的程序员,十分优秀!