QtConcurrent::map()、QtConcurrent::mapped() 和 QtConcurrent::mappedReduced() 函数对一个序列中(例如:QList、QVector)的项目并行地进行计算。
1、map函数
map函数的功能是在其他线程运行指定的函数,map函数有两个参数
第一个是集合
第二个参数是一个函数。它的作用就是同时用第二个参数来计算第一个参数中的每一个元素,且结果直接覆盖到元素中,如果是成员函数,那要静态成员函数才能运行
//静态函数
void Widget::Func(QPushButton * & btn)
{
QTime time = QTime::currentTime();
qsrand(time.msec() + time.second()*1000);
btn->setText(QString("按钮_%1").arg(qrand() % 20));
qDebug()<<"thread ID"<<QThread::currentThreadId();
}
void Widget::on_pushButton_clicked()
{
QList<QPushButton*> list = this->findChildren<QPushButton*>();
QFuture<void> f = QtConcurrent::map(list,&Widget::Func); //map函数 不能运行非静态成员函数
f.waitForFinished();
}
结果:
2、mapped函数
mapped函数的作用和map类似,只是把计算结果放到了新的容器中
例子1:
int func2(int a)
{
return a + 1;
}
void Widget::on_pushButton_clicked()
{
QList<int> alist;
alist<<1<<3<<5<<7<<9;
QFuture<int> f = QtConcurrent::mapped(alist,func2); //QFuture的类型为int
f.waitForFinished();
qDebug()<<"alist"<<alist;
QList<int> newlist = f.results();
qDebug()<<"newlist"<<newlist;
}
结果:
例子2:
QPushButton* Widget::Func2(QPushButton * btn)
{
QThread::msleep(200);
QTime time = QTime::currentTime();
qsrand(time.msec() + time.second()*1000);
btn->setText(QString("按钮_%1").arg(qrand() % 20));
qDebug()<<"thread ID"<<QThread::currentThreadId();
return btn;
}
void Widget::on_pushButton_clicked()
{
QList<QPushButton*> list = this->findChildren<QPushButton*>();
QFuture<QPushButton*> f2 = QtConcurrent::mapped(list,&Widget::Func2);
f2.waitForFinished();
}
结果:
关于mapped,官方有个:Image Scaling Example例子,通过一次性加载多张图片,分别转成100*100的缩略图显示在界面来演示mapped的使用。
//图片转换成100*100的图片
QImage scale(const QString &imageFileName)
{
QImage image(imageFileName);
QThread::msleep(500);
return image.scaled(QSize(imageSize, imageSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
QFutureWatcher imageScaling = new QFutureWatcher<QImage>(this);
connect(imageScaling, &QFutureWatcherBase::resultReadyAt, this, &Images::showImage);
connect(imageScaling, &QFutureWatcherBase::finished, this, &Images::finished);
QStringList files;//图片地址
imageScaling->setFuture(QtConcurrent::mapped(files, scale));
connect(pauseButton, &QAbstractButton::clicked, imageScaling,
&QFutureWatcherBase::togglePaused);//暂停/恢复操作
QFutureWatcherBase::togglePaused 暂停/继续操作
QFutureWatcherBase::resultReadyAt 读取到一个结果
可以用这些信号来设置处理的进度条
用这些信号设置进度条官方正好有个叫:QtConcurrent Progress Dialog Example的简单例子:
void spin(int &iteration)
{
volatile int v = 0;
for (int j = 0; j < 400000000; ++j)
++v;
qDebug() << "处理值" << iteration << "的线程:" << QThread::currentThreadId();
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QVector<int> vector;
for (int i = 0; i < 20; ++i)
vector.append(i);
QProgressDialog dialog;
dialog.setLabelText(QString("正在使用 %1 个线程...").arg(QThread::idealThreadCount()));
QFutureWatcher<void> futureWatcher;
QObject::connect(&futureWatcher, &QFutureWatcherBase::finished, &dialog, &QProgressDialog::reset);
QObject::connect(&dialog, &QProgressDialog::canceled, &futureWatcher, &QFutureWatcherBase::cancel);
QObject::connect(&futureWatcher, &QFutureWatcherBase::progressRangeChanged, &dialog, &QProgressDialog::setRange);
QObject::connect(&futureWatcher, &QFutureWatcherBase::progressValueChanged, &dialog, &QProgressDialog::setValue);
futureWatcher.setFuture(QtConcurrent::map(vector, spin));
dialog.exec();
futureWatcher.waitForFinished();
qDebug() << "Canceled?" << futureWatcher.future().isCanceled();
}
3、mappedReduced函数
mappedReduced函数比mapped多一个参数,这个参数也是个函数。作用就是将mapped出来的结果再计算最终得出一个值。
int func3(int a)
{
return a + 1;
}
void sum(int& result, const int& b)
{
result += b;
}
void Widget::on_pushButton_clicked()
{
QList<int> alist;
alist<<1<<3<<5<<7<<9;
QFuture<int> result = QtConcurrent::mappedReduced(alist,func3,sum);
result.waitForFinished();
qDebug()<<result.result();
}
alist中的一个值执行完func3马上执行sum,而不是alist中所有之都执行完才执行sum。
结果:
关于mappedReduced,官方的demo中有个叫做QtConcurrent Word Count Example的例子通过单线程/多线程统计文件夹中单词个数来演示mappedReduced的使用。
//遍历文件夹,返回文件夹内所有文件名
QStringList findFiles(const QString &startDir, QStringList filters)
{
QStringList names;
QDir dir(startDir);
foreach (QString file, dir.entryList(filters, QDir::Files))
names += startDir + "/" + file;
foreach (QString subdir, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot))
names += findFiles(startDir + "/" + subdir, filters);
return names;
}
//单线程计算此文件列表中每个文件的单词个数:<文件名,单词个数>
QMap<QString, int> singleThreadedWordCount(QStringList & files)
{
QMap<QString, int> wordCount;
QString word;
foreach (QString file, files)
{
QFile f(file);
f.open(QIODevice::ReadOnly);
QTextStream textStream(&f);
while (textStream.atEnd() == false)
foreach(word, textStream.readLine().split(" "))
wordCount[word] += 1;
}
return wordCount;
}
//计算一个文件中单词数
QMap<QString, int> countWords(const QString &file)
{
QFile f(file);
f.open(QIODevice::ReadOnly);
QTextStream textStream(&f);
QMap<QString, int> wordCount;
while (textStream.atEnd() == false)
foreach (QString word, textStream.readLine().split(" "))
wordCount[word] += 1;
return wordCount;
}
void reduce(QMap<QString, int> &result, const QMap<QString, int> &w)
{
QMapIterator<QString, int> it(w);
while (it.hasNext())
{
it.next();
result[it.key()] += it.value();
}
}
int main(int argc, char** argv)
{
QApplication app(argc, argv);
qDebug() << "正在查找文件...";
QStringList files = findFiles("../../",
QStringList() << "*.cpp" << "*.h");//查找此格式的文件
QTime time;
time.start();
QMap<QString, int> total = singleThreadedWordCount(files);
int singleThreadTime = 0;
{
QTime time;
time.start();
QMap<QString, int> total = singleThreadedWordCount(files);
singleThreadTime = time.elapsed();
qDebug() << "单线程统计这些文件单词数所需时间:" << singleThreadTime;
}
int mapReduceTime = 0;
{
QTime time;
time.start();
QMap<QString, int> total = mappedReduced(files, countWords, reduce);
mapReduceTime = time.elapsed();
qDebug() << "MapReduce" << mapReduceTime;
}
qDebug() << "速度提升倍数:" << ((double)singleThreadTime - (double)mapReduceTime) / (double)mapReduceTime + 1;
}
QMap<QString, int> total = mappedReduced(files, countWords, reduce);
这句在多线程里对文件列表中的每个文件执行统计单词操作(执行countWords),然后把统计结果存到QMap中(执行reduce)
打印reduce()中的result的地址可以发现result一直是同一个,猜测:这个QMap<QString, int> &result是mappedReduced申请的,执行reduce时候把数据都保存到这个result里,执行完mappedReduced了就返回值