Qt之Concurrent框架

简述

QtConcurrent 命名空间提供了高级 API,使得可以在不使用低级线程原语(例如:互斥、读写锁、等待条件或信号量)的情况下编写多线程程序,使用 QtConcurrent 编写的程序根据可用的处理器核心数自动调整所使用的线程数。这意味着,当在未来部署多核系统时,现在编写的应用程序将继续适应。

  • 简述
  • 用法
  • Qt Concurrent
  • 单词统计
  • 更多参考

用法

在 C++ API changes 有关于 Qt Concurrent 的更改说明:

Qt Concurrent has been moved from Qt Core to its own module

意思是说,Qt Concurrent 已经被从 Qt Core 中移到自己的模块中了。所以,要链接到 Qt Concurrent 模块,需要在 qmake 项目文件中添加:

QT += concurrent

注意: QtConcurrent::Exception 类被重命名为 QException,并且 QtConcurrent::UnhandledException 类被重命名为 QUnhandledException,他们仍然位于 Qt Core 中。

Qt Concurrent

QtConcurrent 包含了函数式编程风格 APIs 用于并行列表处理,包括用于共享内存(非分布式)系统的 MapReduce 和 FilterReduce 实现,以及用于管理 GUI 应用程序异步计算的类:

  • Concurrent Map 和 Map-Reduce

    • QtConcurrent::map():将一个函数应用于一个容器中的每一项,就地修改 items。
    • QtConcurrent::mapped():和 map() 类似,只是它返回一个包含修改内容的新容器。
    • QtConcurrent::mappedReduced():和 mapped() 类似,只是修改后的结果减少或组合成一个单一的结果。
  • Concurrent Filter 和 Filter-Reduce
    • QtConcurrent::filter():从一个容器中删除所有 items,基于一个 filter 函数的结果。
    • QtConcurrent::filtered():和 filter() 类似,只是它返回一个包含过滤内容的新容器。
    • QtConcurrent::filteredReduced():和 filtered() 类似,只是过滤后的结果减少或组合成一个单一的结果。
  • Concurrent Run
    • QtConcurrent::run():在另一个线程中运行一个函数。
  • QFuture:表示异步计算的结果
  • QFutureIterator:允许通过 QFuture 遍历可用的结果
  • QFutureWatcher:允许使用信号槽来监控一个 QFuture
  • QFutureSynchronizer:是一个方便的类,用于一些 QFutures 的自动同步

Qt Concurrent 支持多种兼容 STL 的容器和迭代器类型,但是最好使用具有随机访问迭代器的 Qt 容器,例如:QList 或 QVector。map 和 filter 函数都接受容器和 begin/end 迭代器。

STL 迭代器支持概述:

迭代器类型 示例类 支持状态
Input Iterator 不支持
Output Iterator 不支持
Forward Iterator std::slist 支持
Bidirectional Iterator QLinkedList, std::list 支持
Random Access Iterator QList, QVector, std::vector 支持和推荐

在 Qt Concurrent 迭代大量轻量级 items 的情况下,随机访问迭代器可以更快,因为它们允许跳过容器中的任何点。此外,使用随机访问迭代器允许 Qt Concurrent 通过 QFuture::progressValue() 和 QFutureWatcher::progressValueChanged() 来提供进度信息。

非就地修改的函数(例如:mapped() 和 filtered()),在调用时会创建容器的副本。如果正在使用的是 STL 容器,此复制操作可能需要一段时间,在这种情况下,建议为容器指定 begin 和 end 迭代器。

单词统计

厉害了 Concurrent,来看一个单词统计的示例:

#include <QList>
#include <QMap>
#include <QTextStream>
#include <QString>
#include <QStringList>
#include <QDir>
#include <QTime>
#include <QApplication>
#include <QDebug>

#include <qtconcurrentmap.h>

using namespace QtConcurrent;

// 递归搜索文件
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;
}

typedef QMap<QString, int> WordCount;

// 单线程单词计数器函数
WordCount singleThreadedWordCount(QStringList files)
{
    WordCount wordCount;
    foreach (QString file, files) {
        QFile f(file);
        f.open(QIODevice::ReadOnly);
        QTextStream textStream(&f);
        while (textStream.atEnd() == false)
            foreach (const QString &word, textStream.readLine().split(' '))
                wordCount[word] += 1;

    }
    return wordCount;
}

// countWords 计算单个文件的单词数,该函数由多个线程并行调用,并且必须是线程安全的。
WordCount countWords(const QString &file)
{
    QFile f(file);
    f.open(QIODevice::ReadOnly);
    QTextStream textStream(&f);
    WordCount wordCount;

    while (textStream.atEnd() == false)
        foreach (const QString &word, textStream.readLine().split(' '))
            wordCount[word] += 1;

    return wordCount;
}

// reduce 将 map 的结果添加到最终结果,该函数只能由一个线程一次调用。
void reduce(WordCount &result, const WordCount &w)
{
    QMapIterator<QString, int> i(w);
    while (i.hasNext()) {
        i.next();
        result[i.key()] += i.value();
    }
}

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    qDebug() << "finding files...";
    QStringList files = findFiles("../../", QStringList() << "*.cpp" << "*.h");
    qDebug() << files.count() << "files";

    int singleThreadTime = 0;
    {
        QTime time;
        time.start();
        // 单线程统计,与 mapreduce 机制实现的作对比
        WordCount total = singleThreadedWordCount(files);
        singleThreadTime = time.elapsed();
        // 打印出所耗费的时间
        qDebug() << "single thread" << singleThreadTime;
    }

    int mapReduceTime = 0;
    {
        QTime time;
        time.start();
        // mappedReduced 方式进行统计
        WordCount total = mappedReduced(files, countWords, reduce);
        mapReduceTime = time.elapsed();
        qDebug() << "MapReduce" << mapReduceTime;
    }
    // 输出 mappedReduced 方式比单线程处理方式要快的倍数
    qDebug() << "MapReduce speedup x" << ((double)singleThreadTime - (double)mapReduceTime) / (double)mapReduceTime + 1;
}

输出如下:

finding files…
2185 files
single thread 5221
MapReduce 2256
MapReduce speedup x 2.31427

可以看出,共查找了 2185 个文件,单线程使用了 5221 毫秒,MapReduce 方式使用了 2256 毫秒,比单线程要快 2.31427 倍。经过反复尝试,基本都在 2 倍以上。

更多参考

时间: 2024-10-01 10:32:05

Qt之Concurrent框架的相关文章

Qt之Concurrent Run

简述 QtConcurrent::run() 函数在一个单独的线程中运行一个函数, 函数的返回值通过 QFuture API 提供. 该函数是 Qt之Concurrent框架 的一部分. 简述 在单独的线程中运行函数 将参数传递给函数 从函数返回值 附加 API 功能 使用成员函数 使用绑定函数参数 更多参考 在单独的线程中运行函数 要在另一个线程中运行函数,使用 QtConcurrent::run(): extern void aFunction(); QFuture<void> futur

Qt之Concurrent Filter和Filter-Reduce

简述 QtConcurrent::filter().QtConcurrent::filtered() 和 QtConcurrent::filteredReduced() 函数对一个序列(例如:QList.QVector )中的项目并行地进行过滤.QtConcurrent::filter() 就地修改一个序列,QtConcurrent::filtered() 返回一个包含过滤内容的新序列,QtConcurrent::filteredReduced() 返回一个单一的结果. 这些函数是 Qt之Con

Qt之Concurrent Map和Map-Reduce

简述 QtConcurrent::map().QtConcurrent::mapped() 和 QtConcurrent::mappedReduced() 函数对一个序列中(例如:QList.QVector)的项目并行地进行计算.QtConcurrent::map() 就地修改一个序列,QtConcurrent::mapped() 返回一个包含修改内容的新序列,QtConcurrent::mappedReduced() 返回一个单一的结果. 这些函数是 Qt之Concurrent框架 的一部分.

qt拖动窗口-linux环境下不能将qt的无框架窗口拖出桌面

问题描述 linux环境下不能将qt的无框架窗口拖出桌面 今天用qt写了一个无框架的窗口,发现在linux环境下不能将其拖出桌面,就是移动到桌面边界时还可以继续移动,窗口有部分被遮住.我问了很多人,都说这是linux环境的bug,请问有代码可以解决吗?可是同样的代码在windows下面就可以,请问怎么解决?

《Qt 实战一二三》

简介 "我们来自Qt分享&&交流,我们来自Qt Quick分享&&交流",不管你是笑了,还是笑了,反正我们是认真的.我们就是要找寻一种Hold不住的状态,来开始每一天的点滴分享,我们是一个有激情,有态度的部队. 但是我们还是我们,我们只是多了一份责任.古语有云:"不积跬步无以至千里,不积小流无以成江海",所以每一个伟大事务的产生都不是一蹴而就的.现在我们要立足眼下,把第一站放在地球,"<Qt 实战一二三>&quo

Qt之QThread(深入理解)

简述 为了让程序尽快响应用户操作,在开发应用程序时经常会使用到线程.对于耗时操作如果不使用线程,UI界面将会长时间处于停滞状态,这种情况是用户非常不愿意看到的,我们可以用线程来解决这个问题. 前面,已经介绍了QThread常用的两种方式: Worker-Object 子类化QThread 下面,我们来看看子类化QThread在日常中的应用. 简述 子类化QThread 线程休眠 在主线程中更新UI 避免多次connect 优雅地结束线程 更多参考 大多数情况下,多线程耗时操作会与UI进行交互,比

Qt Charts - 让数据可视化更简单

简述 Qt Charts 模块提供了一套易于使用的图表组件例如柱状图.面积图.箱形图.饼图.线性图.曲线图.散点图...由于其采用了 Qt Graphics View 框架因此图表可以很容易地被集成到现代的用户界面. Qt Charts 除了图表类型丰富.方便快捷之外还完美支持移动端可用作 QWidgets.QGraphicsWidget 或 QML 类型.通过选择一个图表主题即可轻松地创建令人印象深刻的图表. 在 Qt5.7 之前的版本最早只能到 Qt5.4需要对 Qt Charts 自行编译

Qt Charts

简述 Qt Charts模块提供了一套易于使用的图表组件.它采用了Qt Graphics View框架,因此图表可以很容易地集成到现代的用户界面. Qt Charts可以被用作QWidgets.QGraphicsWidget.或QML类型.用户可以方便地通过选择图表主题之一来创建令人印象深刻的图表. 简述 入门指南 参考资料 示例 安装配置 使用 入门指南 如果要导入Qt Charts QML类型,在你的.qml文件中添加下面的导入语句: import QtCharts 2.0 如果你打算在应用

Qt Charts基本组成

简述 Qt Charts API 构建在 Qt Graphics View 框架之上,可以使用 QChart 类将图表显示为 QGraphicsWidget.但还有一个方便的类 - QChartView,可以作为一个独立的窗口,可以更方便的构建图表,不需要与 QGraphicsScene 一起使用. 所有的图表类型均由 QAbstractSeries 派生类表示.要创建图表,必须使用相关 series 类的实例,并将其添加到 QChart. 简述 Qt Charts API 图表类型 坐标轴 图