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