Qt 线程基础(QThread、QtConcurrent等)

2023-10-29

昨晚看Qt的Manual,突然发现下一个版本的Qt中(Qt4.7.4、Qt4.8等)增加了一个特赞的介绍多线程的文章 :

注意:

  • 该链接以后会失效,但是 到时候你直接看Qt自带Manual就行了
  • 本文不是严格的翻译 dbzhang800 2011.06.18

使用线程

基本上有种使用线程的场合:

  • 通过利用处理器的多个核使处理速度更快。
  • 为保持GUI线程或其他高实时性线程的响应,将耗时的操作或阻塞的调用移到其他线程。

何时使用其他技术替代线程

开发人员使用线程时需要非常小心。启动线程是很容易的,但确保所有共享数据保持一致很难。遇到问题往往很难解决,这是由于在一段时间内它可能只出现一次或只在特定的硬件配置下出现。在创建线程来解决某些问题之前,应该考虑一些替代的技术 

替代技术

注解

QEventLoop::processEvents()

在一个耗时的计算操作中反复调用QEventLoop::processEvents() 可以防止界面的假死。尽管如此,这个方案可伸缩性并不太好,因为该函数可能会被调用地过于频繁或者不够频繁。

QTimer

后台处理操作有时可以方便地使用Timer安排在一个在未来的某一时刻执行的槽中来完成。在没有其他事件需要处理时,时间隔为0的定时器超时事件被相应

QSocketNotifier 
QNetworkAccessManager 
QIODevice::readyRead()

这是一个替代技术,替代有一个或多个线程在慢速网络执行阻塞读的情况。只要响应部分的计算可以快速执行,这种设计比在线程中实现的同步等待更好。与线程相比这种设计更不容易出错且更节能(energy efficient)。在许多情况下也有性能优势。

一般情况下,建议只使用安全和经过测试的方案而避免引入特设线程的概念。QtConcurrent 提供了一个将任务分发到处理器所有的核的易用接口。线程代码完全被隐藏在 QtConcurrent 框架下,所以你不必考虑细节。尽管如此,QtConcurrent 不能用于线程运行时需要通信的情况,而且它也不应该被用来处理阻塞操作。

应该使用 Qt 线程的哪种技术?

有时候,你需要的不仅仅是在另一线程的上下文中运行一个函数。您可能需要有一个生存在另一个线程中的对象来为GUI线程提供服务。也许你想在另一个始终运行的线程中来轮询硬件端口并在有关注的事情发生时发送信号到GUI线程。Qt为开发多线程应用程序提供了多种不同的解决方案。解决方案的选择依赖于新线程的目的以及线程的生命周期。

生命周期

开发任务

解决方案

一次调用

在另一个线程中运行一个函数,函数完成时退出线程

编写函数,使用QtConcurrent::run 运行它

派生QRunnable,使用QThreadPool::globalInstance()->start() 运行它

派生QThread,重新实现QThread::run() ,使用QThread::start() 运行它

一次调用

需要操作一个容器中所有的项。使用处理器所有可用的核心。一个常见的例子是从图像列表生成缩略图。

QtConcurrent 提供了map()函你数来将操作应用到容器中的每一个元素,提供了fitler()函数来选择容器元素,以及指定reduce函数作为选项来组合剩余元素。

一次调用

一个耗时运行的操作需要放入另一个线程。在处理过程中,状态信息需要发送会GUI线程。

使用QThread,重新实现run函数并根据需要发送信号。使用信号槽的queued连接方式将信号连接到GUI线程的槽函数。

持久运行

生存在另一个线程中的对象,根据要求需要执行不同的任务。这意味着工作线程需要双向的通讯。

派生一个QObject对象并实现需要的信号和槽,将对象移动到一个运行有事件循环的线程中并通过queued方式连接的信号槽进行通讯。

持久运行

生存在另一个线程中的对象,执行诸如轮询端口等重复的任务并与GUI线程通讯。

同上,但是在工作线程中使用一个定时器来轮询。尽管如此,处理轮询的最好的解决方案是彻底避免它。有时QSocketNotifer是一个替代。

Qt线程基础

QThread是一个非常便利的跨平台的对平台原生线程的抽象。启动一个线程是很简单的。让我们看一个简短的代码:生成一个在线程内输出"hello"并退出的线程。

 // hellothread/hellothread.h
 class HelloThread : public QThread
 {
     Q_OBJECT
 private:
     void run();
 };

我们从QThread派生出一个类,并重新实现run方法。

 // hellothread/hellothread.cpp
 void HelloThread::run()
 {
      qDebug() << "hello from worker thread " << thread()->currentThreadId();
 }

run方法中包含将在另一个线程中运行的代码。在本例中,一个包含线程ID的消息被打印出来。  QThread::start() 将在另一个线程中被调用。

 int main(int argc, char *argv[])
 {
     QCoreApplication app(argc, argv);
     HelloThread thread;
     thread.start();
     qDebug() << "hello from GUI thread " << app.thread()->currentThreadId();
     thread.wait();  // do not exit before the thread is completed!
     return 0;
 }

QObject与线程

QObject有线程关联(thread affinity)[如何翻译?关联?依附性?dbzhang800 20110618],换句话说,它生存于一个特定的线程。这意味着,在创建时QObject保存了到当前线程的指针。当事件使用postEvent()被派发时,这个信息变得很有用。事件被放置到相应线程的事件循环中。如果QObject所依附的线程没有事件循环,该事件将永远不会被传递。

要启动事件循环,必须在run()内调用exec()。线程关联可以通过moveToThread()来更改。

如上所述,当从其他线程调用对象的方法时开发人员必须始终保持谨慎。线程关联不会改变这种状况。 Qt文档中将一些方法标记为线程安全。postEvent()就是一个值得注意的例子。一个线程安全的方法可以同时在不同的线程被调用。

通常情况下并不会并发访问的一些方法,在其他线程调用对象的非线程安全的方法在出现造成意想不到行为的并发访问前数千次的访问可能都是工作正常的。编写测试代码不能完全确保线程的正确性,但它仍然是重要的。在Linux上,Valgrind和Helgrind有助于检测线程错误。

QThread的内部结构非常有趣:

  • QThread并不生存于执行run()的新线程内。它生存于旧线程中。
  • QThread的大多数成员方法是线程的控制接口,并设计成从旧线程中被调用。不要使用moveToThread()将该接口移动到新创建的线程中;调用moveToThread(this)被视为不好的实践。
  • exec()和静态方法usleep()、msleep()、sleep()要在新创建的线程中调用。
  • QThread子类中定义的其他成员可在两个线程中访问。开发人员负责访问的控制。一个典型的策略是在start()被调用前设置成员变量。一旦工作线程开始运行,主线程不应该操作其他成员。当工作线程终止后,主线程可以再次访问其他成员。这是一个在线程开始前传递参数并在结束后收集结果的便捷的策略。

QObject必须始终和parent在同一个线程。对于在run()中生成的对象这儿有一个惊人的后果:

 void HelloThread::run()
 {
      QObject *object1 = new QObject(this);  //error, parent must be in the same thread
      QObject object2;  // OK
      QSharedPointer <QObject> object3(new QObject); // OK
 }

使用互斥量保护数据的完整

互斥量是一个拥有lock()和unlock()方法并记住它是否已被锁定的对象。互斥量被设计为从多个线程调用。如果信号量未被锁定lock()将立即返回。下一次从另一个线程调用会发现该信号量处于锁定状态,然后lock()会阻塞线程直到其他线程调用unlock()。此功能可以确保代码段将在同一时间只能由一个线程执行。

使用事件循环防止数据破坏

Qt的事件循环对线程间的通信是一个非常有价值的工具。每个线程都可以有它自己的事件循环。在另一个线程中调用一个槽的一个安全的方法是将调用放置到另一个线程的事件循环中。这可以确保目标对象调用另一个的成员函数之前可以完成当前正在运行的成员函数。

那么,如何才能把一个成员调用放于一个事件循环中? Qt的有两种方法来做这个。一种方法是通过queued信号槽连接;另一种是使用QCoreApplication::postEvent()派发一个事件。queued的信号槽连接是异步执行的信号槽连接。内部实现是基于posted的事件。信号的参数放入事件循环后信号函数的调用将立即返回。

连接的槽函数何时被执行依赖于事件循环其他的其他操作。

通过事件循环通信消除了我们使用互斥量时所面临的死锁问题。这就是我们为什么推荐使用事件循环,而不是使用互斥量锁定对象的原因。

处理异步执行

一种获得一个工作线程的结果的方法是等待线程终止。在许多情况下,一个阻塞等待是不可接受的。阻塞等待的替代方法是异步的结果通过posted事件或者queued信号槽进行传递。由于操作的结果不会出现在源代码的下一行而是在位于源文件其他部分的一个槽中,这会产生一定的开销,因为,但在位于源文件中其他地方的槽。 Qt开发人员习惯于使用这种异步行为工作,因为它非常相似于GUI程序中使用的的事件驱动编程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt 线程基础(QThread、QtConcurrent等) 的相关文章

  • 在 Qt GraphicsView 中创建长线(或十字线)光标的最佳方法

    创建长十字线光标 与视口一样长 的简单方法是创建一条十字线graphicsItem 当鼠标移动时 设置该项目的pos财产 但是当场景复杂时这种方式会很慢 因为它要更新整个视口来更新光标的pos 另一种简单的方法是setCursor QCur
  • 为什么动态 qml 对象的创建如此缓慢,有哪些合理的替代方案?

    我想要实现的目标类似于棋盘游戏 有一个100 100的网格 放在一个Item它驻留在一个Flickable 游戏板 的各个矩形都是 svg 图像 目前大约有 20 种 可能会增加到数百种 作为基准测试 我只是尝试用元素填充 世界 Compo
  • QML MouseArea 将事件传播到按钮

    我正在开发一个应用程序 其菜单类似于 Android 版 Gmail 收件箱应用程序菜单 基本上 当您按下按钮打开菜单时 它就会滑入视图 用户可以将其滑开或按菜单上的按钮 对于滑动我使用了代码SwipeArea from kovrov ht
  • QWebEngineView 在 load() 或 page() 方法上崩溃

    我正在致力于将 Qt 5 5 QWebView 项目移植到 Qt 5 6 测试版 QWebEngine 我已经阅读了移植指南here https wiki qt io Porting from QtWebKit to QtWebEngine
  • 如何在不声明 32 个插槽的情况下将 32 个按钮的 pressed() 信号连接到单个函数?

    我有一个小部件 里面有 32 个按钮 我需要将每个按钮的 Pressed 信号连接到一个插槽 以便调用一个函数 该函数的参数取决于我按下的按钮 现在我通过以 on QPushButtonName pressed 的形式添加 32 个插槽来做
  • 为按钮控件 Qt Widgets 样式表定义多种样式

    如何在一个样式表中为一种控件定义多种样式 这样以后开发人员就可以选择控件应该是什么样子的样式 例如 我需要定义两种样式QPushButton 用于普通按钮 左侧 和操作按钮 右侧 对于第一个按钮 我编写了以下样式 QPushButton b
  • 当选项卡到另一个组件位置时,QML 中相应的滚动

    我想做的是 如果我从TextField到另一个组件 aComboBoxwtv 我希望滚动能够适应这一点 当我认为这非常重要时 当我执行连续选项卡时 我会转到滚动视图显示的内容下方的控件 一个例子是 假设我在这里 now i do 2 tab
  • Qt - 添加超链接到对话框

    有没有办法在 Qt 对话框中添加可点击的超链接 IE 它应该看起来像一个超链接 蓝色文本 当您单击它时 它应该在浏览器中打开该超链接 像这样的东西 Use QLabel setOpenExternalLinks bool 并在标签上设置文本
  • 如何为QTableView中的当前项目设置样式表

    When QTableView编辑控件对于发生编辑的当前项目可见 当窗口中没有活动的编辑控件时QTableView当前项目的样式使用QTableView selection background color 如何只为当前项目设置不同的样式
  • Qt:使用 QObject::connect 指定多种连接类型

    我想知道是否可以指定多种连接类型 例如 我希望我的连接类型是排队连接和唯一连接 是否可以在一份声明中具体说明这一点 QObject connect ptrSender SIGNAL ptrReceiver SLOT Queued and u
  • QDateTime::fromstring( __DATE__, "MMM d yyyy") 返回无效

    解析MSVC 预定义 DATE 也许结合 TIME 宏与 QDateTime fromstring 不返回任何内容 无效的 QDateTime 对象 为什么 From http msdn microsoft com en us librar
  • QSettings - ini 文件的位置在哪里?

    我在用着QSettings在 Windows 中将一些数据存储为 ini 文件 我想查看ini文件 但我不知道ini文件的位置在哪里 这是我的代码 QSettings set new QSettings QSettings IniForma
  • Qt 要包含哪些标头?

    使用 Qt 使用 C 进行编程时 我应该包含哪些内容 仅包含 2 个 include
  • 如何禁用滚轮事件 QGraphicsView Qt C++ 上的滚动功能

    我有一个图形视图 并且我设置了自己的功能 以便在用户按下 Control 并单击鼠标时手动拖动滚动 我已经删除了滚动条 但鼠标滚轮仍然会滚动 甚至滚动到显示在图像中的图像qGraphicsView显示我的手拖动没有的空白 白色 空间 我怎样
  • 在 Qt 5.12 的 OpenCV dll 上找不到入口点

    TLDR 将 OpenCV 与 QT MingW 链接会使应用程序在调试时崩溃 但不会在发布时崩溃 我正在尝试在基于 Qt 的大型多操作系统项目中使用 OpenCV 我已经轻松地为 Mac 和 Linux 构建了 OpenCV 但在 Win
  • QTextEdit:仅当滚动条位于底部时自动向下滚动

    有一个 QTextEdit 显示相当多的文本 它不可编辑 假设我想在开头阅读一些内容 向上滚动 但随后添加了一个新行 并且滚动条自动转到底部 我在使用各种程序时遇到类似的问题 无论它们是用什么语言编写的 如何处理这个问题 当向文本添加新行时
  • QTreeView绘制下降指示器

    我需要在 QTreeView 中通过拖放实现行移动 并显示行之间的放置指示器 我想知道是否有一种方法可以覆盖指示器绘制 因此它仅针对行之间的所有层次结构级别显示 而不是项目周围的矩形 该线必须与整行一样宽 而不是与一列一样宽 可以通过修改用
  • 防止 QGraphicsItem 移出 QGraphicsScene

    我有一个场景 其固定尺寸从 0 0 到 481 270 scene gt setSceneRect 0 0 481 270 在里面 我有一个习惯GraphicsItem多亏了旗帜我可以移动它ItemisMovable 但我希望它留在场景中
  • Qt jpg图像显示

    我想在 Qt UI 中显示 jpg 图像 我上网查了一下 发现https doc qt io archives qt 4 8 qt widgets imageviewer example html https doc qt io archi
  • 如何将 QSerialPort 模块添加到 CMake 中?

    我想将 QSerialPort 模块添加到 CMake 中 根据我的理解 我需要将QT 串口添加到 pro中 我只想使用 CMake 所以我尝试编译简单的 CMake 文件 但有错误 QtCore 正在工作 qDebug 可以毫无问题地显示

随机推荐

  • 十大C++实战项目,你会几个?【高薪必备】

    市面上有很多C 的实战项目 从简单到进阶 学习每个项目都可以掌握相应的知识点 如果你还是C 新手的话 那么这个C 的项目列表你可以拿去练手实战开发 毕竟学编程动手实践是少不了的 如果你不知道C 可以用来做哪些项目 可以应用在哪些地方 那么
  • 解决临时表空间不足

    第一种方法 数据库服务器切换到 oracle的根目录执行 su oracle oracle edzxbsdb source bash profile oracle edzxbsdb sqlplus as sysdba 进入sql SQL g
  • bat脚本-卸载并重新安装apk,强制关闭app并重新启动app

    卸载并重新安装apk echo off echo echo Get devices adb devices gt devices txt echo echo restartApp for f skip 1 tokens 1 delims i
  • 头条号如何快速涨100W+粉丝?

    最近一些做头条的朋友和我反映 最近头条的流量很不错 给账号的扶持很大 劝诫我们要抓住这次机会 01 提高爆文产出率 粗看是句废话 但其中藏有奥妙 依靠爆款优质内容涨粉看似 低效 但始终是最根本的途径 由此吸引的粉丝 忠诚度极高 小易这头条号
  • Go【gin和gorm框架】实现紧急事件登记的接口

    简单来说 就是接受前端微信小程序发来的数据保存到数据库 这是我写的第二个接口 相比前一个要稍微简单一些 而且因为前端页面也是我写的 参数类型自然是无缝对接 前端页面大概长这个样子 先用apifox模拟发送请求测试 apifox可以直接复制J
  • python 字符串长度

    Python是一种高级编程语言 它具有简单易学 可读性强 功能强大等特点 因此在各个领域都有广泛的应用 在Python中 字符串是一种非常重要的数据类型 它可以用来存储文本信息 比如说一段话 一篇文章等等 字符串的长度是指其中字符的个数 可
  • mysql查询 多门课程的平均成绩_数据分析中级 MySQL 任务6 总结复习

    0 入门 0 1 MySQL安装 Navicat安装 0 2 MySQL设置 Nacicat设置 包括链接点 unicode 8 0 3 创建表格 student course score teacher 1 简单查询 1 1 查询姓 猴
  • 2023年7月婴幼儿辅食市场数据分析(京东商品数据)

    随着人们对婴幼儿饮食健康的关注不断增加 市场对高品质 安全 营养丰富的辅食需求也日益旺盛 婴幼儿辅食市场增长放缓 但整体仍保持上升态势 鲸参谋数据显示 今年7月份 京东平台婴幼儿辅食市场的销量为1000万 同比增长约8 本月的总销额为3 7
  • ORM如何处理many -to -many的关系

    表之间的关联可以形成一张非常复杂的graph 但是我们对其进行抽象就会发现两个有关系的表之间只有两种可能 one to many 或者many to many many to many 时会加入一个关联表 所以这里讲述的是如何处理关联表映射
  • (一)pytorch单任务图像分类

    深度学习主要由 数据读取 网络模型 损失函数 优化器这四个部分构成 最开始不应该纠结于这些细节 应该先让代码跑起来再去研究代码是怎么写的 下面的代码只是训练部分的代码 并加上验证模型准确率的功能 1 项目分布 创建一个文件夹my data1
  • muduo_base库学习笔记7——无界队列、有界队列及线程池的实现

    BlockingQueue和BoundedBlockingQueue实质就是一个生产者消费者的模型 一 BlockingQueue 只用了一个条件变量notEmpty 不需要notFull 因为无界嘛不用考虑满的情况 条件变量需要跟一个互斥
  • (转)如何正确地处理时间

    from http www liaoxuefeng com article 0014132675721847f569c3514034f099477472c73b5dee2000 日期和时间在程序中应用广泛 每种程序开发语言都自带处理日期和时
  • 正则表达式简单判断

    正则表达式简单判断 1 正则表达式 d 非负整数 正整数 0 0 9 1 9 0 9 正整数 d 0 非正整数 负整数 0 0 9 1 9 0 9 负整数 d 整数 d d 非负浮点数 正浮点数 0 0 9 0 9 1 9 0 9 0 9
  • python操作word文档(python-docx)

    python操作word文档 python docx 1 效果图 1 1 python docx文档标题段落 等级 加粗 斜体 居中 效果图 1 2 python docx字体 加粗 斜体 居中 大小颜色 高亮颜色 效果图 1 3 pyth
  • MVS(多视图)

    1 传统方法通过几何 光学一致性构造匹配 估计深度 在缺少纹理或光照条件剧烈变化的场景中发生错误匹配 2 MVS流程 数据采集 稀疏重建 深度图估计 稠密重建 3 增量式SFM 输出的3D 点云 可以重新估计相对位姿 修正位姿结果 4 MV
  • git stash命令用法详解(临时存储代码)

    1 需求背景 有时候在开发过程中 在一个分支上 dev1 已经写了一部分代码 但是需要紧急切换到别的分支 dev2 上修改某个代码 这时候不能直接从dev1分支上切换到dev2分支上 提示你需要保存代码 此时dev1分支上的代码还没有写完
  • ChatGPT帮忙写一篇Font Awesome的文章,效率有多高?(附加Font Awesome 图标无法正确显示解决方法)

    目录 一 ChatGPT撰写文章 二 图标无法正确显示以及解决方法 一 ChatGPT撰写文章 Font Awesome是一种流行的图标字体和CSS工具包 它允许开发人员轻松地将可伸缩向量图标和社交徽标添加到他们的网站 它由Dave Gan
  • Windows10蓝屏触发及分析

    一 重现环境 1 windows10版本 2 idapro7 5499 3 vs2008运行库 vcredist2008sp1 zip 二 触发蓝屏 1 触发poc include
  • 企业微信php 通讯录管理系统,微信企业号管理通讯录

    微信企业号管理通讯录 2018 09 16 还没吃饭呢 刚写了下企业号通讯录接口 企业号通讯录具备完全开放的接口 你的应用可以调用这些接口管理部门 成员和标签 你的应用也可以使用部门 成员 标签发消息 或更改应用的可见范围 在通讯录管理下
  • Qt 线程基础(QThread、QtConcurrent等)

    昨晚看Qt的Manual 突然发现下一个版本的Qt中 Qt4 7 4 Qt4 8等 增加了一个特赞的介绍多线程的文章 Thread Basics 注意 该链接以后会失效 但是 到时候你直接看Qt自带Manual就行了 本文不是严格的翻译 d