Qt 多线程之QtConcurrent::map(处理序列容器)

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了就返回值

原文地址:https://www.cnblogs.com/ybqjymy/p/14150129.html