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