目录

前言

一、简介

1.1 并发执行机制

1.2 多线程机制

1.3 多线程处理的方式

二、Qt高级线程类实现多线程

2.1 在线程中执行函数

2.1.1 使用QtConcurrent::run()

2.1.2 使用QRunnable

2.1.3 示例代码

2.2 线程中的过滤和映射

2.2.1 过滤器的使用

2.2.2 映射器的使用

2.2.3 简化器的使用

2.2.4 示例代码

三、QThread类实现多线程

3.1 独立项的处理

3.2 共享项的处理

总结


前言

周末在图书馆不经意间翻阅了《Qt高级编程》后就爱释手,作者Mark Summerfield高瞻远瞩、思路清晰,刚好满足我提升Qt知识的需要,于是立马从图书馆借出,舔了三月。


一、简介

1.1 并发执行机制

一个处理器划分为若干个短的时间片,每个时间片依次轮流地执行处理各个应用程序,由于一个时间片很短,相对于一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序在同时进行的效果。

1.2 多线程机制

多线程就是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程。

对比单线程来分析,多线程的优缺点如下:

优点:

  1. 通过将耗时长的任务放入辅助线程运行来避免冻结用户界面;
  2. 程序的运行速度可能加快;
  3. 任务执行能随时中断;
  4. 可设置各个任务的优先级;

缺点:

  1. 占用和消耗更多的内存空间;
  2. 增加了程序的调试复杂度;
  3. 增加了程序的操作风险度,容易发生死锁而导致程序崩溃;

1.3 多线程处理的方式

可以把处理的数据分配给一些线程来执行,有三种方式:

  1. 利用QtConcurrent::run()在Qt全局线程池的辅助线程中运行函数来处理;
  2. 创建QRunnable对象并在Qt全局线程池辅助线程来处理;
  3. 创建QThread对象并把它作为辅助线程来处理;

二、Qt高级线程类实现多线程

2.1 在线程中执行函数

2.1.1 使用QtConcurrent::run()

QtConcurrent 是命名空间 ,它提供了高层次的函数接口 (APIs),使所写程序,可根据计算机的 CPU 核数,自动调整运行的线程数目。QtConcurrent::run()函数的参数包含一个函数、一个或多个传递给函数的可选参数,它会在Qt全局线程池中的一个辅助线程中执行该函数,函数如下:

typedef int Function;namespace QtConcurrent {template <typename T>QFuture<T> run(Function function, ...);template <typename T>QFuture<T> run(QThreadPool *pool, Function function, ...);} // namespace QtConcurrent

函数返回T型对象,Function是一个指向仿函数的指针,省略号代表一个或多个变量参数,当函数被QtConcurrent调用时,会把它们传递给Function,且参数必须与Function的形式相匹配。

由于QtConcurrent不继承QObject,所以不支持信号槽机制。它有个明显的缺点:没有提供进度通知功能和结束通知功能。

解决函数在辅助线程中的进度通知,可以使用自定义事件,实现的方案如下:

  1. 定义一个自定义事件;
  2. 主窗口中重载event()方法;
  3. 线程函数中使用QApplication::postEvent()方法发送自定义事件。

解决函数在辅助线程中的结束通知,有两种方式:

  1. 使用QTimer::singleShot()轮询结果;
  2. 使用QFutureWatcher<T>查询辅助线程状态。

2.1.2 使用QRunnable

QRunnable类在Qt中是全部可运行对象的基类,QRunnable类如下:

class Q_CORE_EXPORT QRunnable
{int ref;friend class QThreadPool;friend class QThreadPoolPrivate;friend class QThreadPoolThread;
public:virtual void run() = 0;QRunnable() : ref(0) { }virtual ~QRunnable();bool autoDelete() const { return ref != -1; }void setAutoDelete(bool _autoDelete) { ref = _autoDelete ? 0 : -1; }
};

QRunnable类的run()函数表示一个任务,任务被分配在Qt 全局线程池中的一个线程中。

因为QRunnable不继承QObject,所以不内置支持信号槽机制,要实现进度通知功能则可以采用自定义事件或调用主窗口的槽函数来实现。自定义事件的使用在上一章节已经介绍,此处不重复讲解,使用主窗口槽函数的方法如下:

  1. 主窗口中定义一个槽函数,用于进度显示;
  2. 实现一个继承QRunnable类的任务对象,
  3. 任务对象支持通过主窗口指针调用槽函数来实现跨线程通信;
  4. 调用QThreadPool::start()方法来启动辅助线程执行任务;

2.1.3 示例代码

使用QtConcurrent::run()和QRunnable类实现在线程中执行函数的示例代码,可从链接下载:

https://pan.baidu.com/s/1yuOfHZSb4a6XsKRZ2vOC-g
提取码:sk45

2.2 线程中的过滤和映射

QtConcurrent提供四种函数:运行函数、过滤器、映射器和简化器。

当有大量数据项需要处理,如果为每个项都创建一个线程将会导致大量的系统开销,因此,我们可以采取依次处理数据,创建少量的辅助线程并让每个线程只处理一组项。这就是过滤器、映射器和简化器的使用场景。

QtConcurrent的过滤器和映射器遵守函数式编程方法。

2.2.1 过滤器的使用

过滤器带一个数据项的序列,过滤器函数标记符合条件的数据项且将它们输出到一个新的数据项序列,从概念上来分析,它的工作方式表示如下:

// FilterQList<Item> filter<QList<Item> items, FilterFunction isOK)
{QList<Item> results;foreach (Item item, items){if (isOK(item)){results << item;}}return results;
}

2.2.2 映射器的使用

映射器带一个序列和一个映射函数,通过对原始数据项序列进行映射,并产生一个新序列且新序列的项数与原始序列相同。从概念上来分析,它的工作方式表示如下:

// MapperQList<TypeNew> filter<QList<Type> items, MapFunction mapFun)
{QList<TypeNew> results;foreach (Type item, items){results << mapFun(item);}return results;
}

2.2.3 简化器的使用

简化器带一个项序列,将序列处理或汇总成单一的项。可以将过滤与简化结合,或映射与简化结合。从概念上来分析,它的工作方式表示如下:

// Custom structure
struct ResultType
{ResultType() {sum = 0;}void mergeFun(int item){sum += item;}int sum;
};// Filter-Reduce
ResultType  filter_reduce<QList<Item> items, FilterFunction isOK)
{ResultType result;foreach (Item item, items){if (isOK(item)){result.MergeFun(item)}}return result;
}// Map-Reduce
ResultType  filter_reduce<QList<Item> items, MapFunction mapFun)
{ResultType result;foreach (Item item, items){result.mergeFun(mapFun(item))}return result;
}

2.2.4 示例代码

使用QtConcurrent::run()和QRunnable类实现过滤器、映射器和简化器的示例代码,参考链接:

https://pan.baidu.com/s/1OoIXDuhtAgdaMhKx6Alcwg
提取码:7ubf

三、QThread类实现多线程

当少数项需要后台处理时,可以使用QThread来追踪进度和进度完成情况。QThread继承QObject类和支持Qt的信号和槽机制,我们可以用子类重新实现run()方法,并通过调用start()方法来启动线程。根据处理项的访问特征,可分成独立项处理和共享项处理两种方法。

3.1 独立项的处理

如果需要处理的项较少,且所有的处理项都是独立的,可以使用使用独立项的处理方法。每个处理项使用一个单独的QThread子类实例来进行处理。所有处理过程独立完成,不需要锁。

针对独立项的处理,作者编写了"Cross Fader”应用程序,此程序让用户选择两幅图像,然后通过建立一个用户定义的中间图像数量来生成一个平滑过滤的图像序列。

输入开始图像和结束图像,使用4个独立线程来生成4幅平滑过渡中间图,效果如下:

详细的QThread类独立项处理方法可参考crossfader示例源码,下载地址如下:

链接:https://pan.baidu.com/s/1tLTZU_bjkvZYzNAEHtGUdw
提取码:jpao

3.2 共享项的处理

如果需要处理的项比较多,而且不知道项的大小,使用共享的工作序列能较好的处理这种情况。先将工作任务放到序列中,并启动一定数量的辅助线程先来开始数据处理,然后再逐步添加其它工作任务。每个工作序列内置锁定且是线程安全的,每个访问序列的辅助线程将其看作是特定类型的数据结构。

针对共享项的处理,作者编写了"Find Duplicates”应用程序,此应用程序在用户给定的目录和其子目录内查找重复的文件,通过分析每个文件的大小和MD5签名来确定文件是否重复。此应用程序的运行效果图如下:

本例的实现思路如下:

在用户选择的目录中创建一个子目录列表,将这个列表尽可能平均分配到辅助线程中,每一个辅助线程都会向一个共享的哈希表添加条目,这些条目是它所处理的每个目录中的每个文件及每个子目录中的文件,为了确保工作公平分配,创建一个单独的数据结构,它包含所有的文件并根据文件的尺寸和文件名分配到线程,并发执行线程后并生成结果。

详细的QThread类共享项处理方法可参考findduplicates示例源码,下载地址如下:

链接:https://pan.baidu.com/s/1myD_2SxIEPUU2PbfaeUHhw
提取码:55k1


总结

本书的多线程的示例源码基本上过了一遍,但其中的精髓还没有完全领悟,编写拥有多线程处理功能的程序比单线程要难度大很多,且有出现死锁的风险,务必小心谨慎。

Qt高级编程之多线程处理相关推荐

  1. Qt-------->第六天,Qt高级编程

    1 IO 进程 线程 IO------->Qt第五天笔记     QFile file("1.txt");file.open();file.read();file.write ...

  2. QT高级编程之基本函数用法

    1.QString类函数介绍 toInt()函数,可以将整型按照不同进制转换为QString对象: number()函数,可以将QString对象转换为不同进制的数字: 2.文档查询方法:通过Qt C ...

  3. QT高级编程之QT基本概览

    QT高级编程 主要从以下几个方面来介绍QT高级编程,并介绍QT相关的概念. 1. QT部件Widget: 2. QT信号与槽机制: 3. 对象树关系: 4. 布局管理: 5.标准对话框以及自定义对话框 ...

  4. Qt高级编程之MVC框架

    目录 前言 一.MVC简介 二.MVC架构 2.1 MVC层级关系图 2.2 MVC类结构图 三.模型/视图表格 3.1 标准表格模型 3.1.1 应用场景 3.1.2 数据过滤 3.1.3 QSta ...

  5. 嵌入式系统开发学习步骤(Linux高级编程学习顺序)

    2019独角兽企业重金招聘Python工程师标准>>> 嵌入式系统开发学习步骤(Linux高级编程学习顺序) 1.Linux 基础 安装Linux操作系统 Linux文件系统 Lin ...

  6. 深入浅出Qt数据库编程:从基本操作到高级技巧

    深入浅出Qt数据库编程:从基本操作到高级技巧 (Demystifying Qt Database Programming: From Basic Operations to Advanced Tech ...

  7. 来吧,嘤!,c++高级编程介绍

    c++高级编程介绍 学c++确实是件痛苦的事,这水平得一步步抬上去,实话说学校教的也不好,就更痛苦了,还要学这学那,对技术没半点提升.最近就在学这个,没得方向,只好自己归纳了.嘤!嘤! 目录 预处理 ...

  8. Qt网络编程概述(一)

    分享主题 Qt网络编程概述(一) Qt网络编程之QTCPSocket和QTCPServer实例(二) Qt网络编程之QUdpSocket实例(三) Qt网络编程概述 QtNetWork模块提供了若干类 ...

  9. Go 学习推荐 —(Go by example 中文版、Go 构建 Web 应用、Go 学习笔记、Golang常见错误、Go 语言四十二章经、Go 语言高级编程)

    Go by example 中文版 Go 构建 Web 应用 Go 学习笔记:无痕 Go 标准库中文文档 Golang开发新手常犯的50个错误 50 Shades of Go: Traps, Gotc ...

最新文章

  1. 【强势来袭】Node.js(nodejs)实现“一口多用”(含用户创建、登录、鉴权token) 一个文件解决所有常态化需求
  2. 【清华集训2017】榕树之心
  3. 最小生成树(Prim、Kruskal)算法,秒懂!
  4. k8s概念: service和ingress
  5. 初学Java,这三个阶段你经历过吗?
  6. CRMEB页面说明这个是v3.0H5端的
  7. 一文详解 Prometheus 的高可用方案:Thanos
  8. 安卓拒绝服务漏洞分析及漏洞检测
  9. codeforces1451 D. Circle Game
  10. 嘉益仕(Litns)带您读懂MES系统:选型篇
  11. 【LeetCode】Minimum Depth of Binary Tree 二叉树的最小深度 java
  12. Qt工作笔记-跑马灯效果
  13. DataTable中Compute计算函数
  14. python异步高并发_python高并发异步服务器核心库forkcore使用方法
  15. flink其他可选api
  16. [Offer收割]编程练习赛12 题目1 : 歌德巴赫猜想
  17. oracle 比较日期相等
  18. 使用监控宝监控php-fpm状态
  19. 线性表的顺序存储结构和链式存储结构
  20. 目标检测:损失函数之SmoothL1Loss

热门文章

  1. 关于土豆网盈利模式的一点儿构思
  2. 【重磅】4 款超好用的在线视频转GIF神器推荐!!
  3. typora 浏览器预览_功能演示丨Adobe Xd安卓手机实时预览教程
  4. 100句非常精辟的励志语录(1)
  5. code 128 ....command git --no-replace-objects ls-remote ssh://git@github.com/nhn/raphael.git解决方案
  6. 结合directx3D函数库3D视角游戏(软件工程第二次作业)
  7. LCS/最长公共子序列/最长公共子串 实现 Python/Java
  8. 多数投票算法 Majority Vote Algorithm
  9. 从鸿蒙发布看互联网周边生态,拍照性能双升级 倪妮代言vivo X9s上手体验
  10. QT控件通过setProperty设置属性显示内容