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

  • 相关阅读:
    Spring源码IOC容器初始化过程【2】
    《MySQL实战45讲》个人笔记实战篇
    Spring源码Xml Bean解析注册过程【3】
    Spring源码循环依赖用实例证明去掉二级缓存会出现什么问题【7】
    Spring源码Bean实例化过程【5】
    git删除已push记录
    3.多线程面试
    4.JVM面试题
    6.Linux文本处理
    二.实践中发现的命令问题
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14150129.html
Copyright © 2020-2023  润新知