多线程Qt下的八条规则 qt多线程处理数据
liebian365 2024-10-17 14:00 25 浏览 0 评论
相信资深Qter都认识Giuseppe D’Angelo,这位有着二十多年Qt开发经验,Qt源码行数贡献的最多的开发者之一,同时也是Qt项目的审批者,所说话的份量不言而喻。
原文作者: 朱塞普 D 安吉洛(Giuseppe D’Angelo)
多线程一直是一个老生常谈的话题,而Qt作为C++生态非常重要的一环,熟练使用多线程是必备的技能。
Qt中封装了很多多线程的轮子,我们在日常开发中,除了要学会怎么用之外,如何用好才是关键。
下面来看一下可以代表Qt官方的Giuseppe D’Angelo是如何告诫大家的。
尽管多线程的概念看起来非常简单,但在日常编码中,使用多线程总会遇到难以复现和追踪的恶心BUG。这导致我们使用多线程写出可靠代码是非常困难的。接下来让我们深入了解一下,为什么会出现这种状况。
首先你需要对框架底层、编程语言、编译器的有深层次的理解,这样你才能知道如何避免线程高发问题;其次,你还要理解同步原语,使用合适的设计模式来编写多线程代码,只有这样才能使多业务在所有条件下正常执行;最后,你还要学会使用调试工具来调试多线程,只有这样你才能发现多线程内部难以复现的BUG。
当涉及到Qt多线程后,尤其需要了解你的框架和设计模式。Qt能让你写出令人惊奇的多线程程序,也能让你开枪打自己的脚。多年的Qt框架、Qt客户端多线程BUG查找和修复经验磨练了我们多线程编码水平,这里有几条我们总结出来的顶级信条,它可以让你避免多线程常见问题,可以让你的程序在第一时间正常运行。
1. 避免使用QThread::sleep()
尽管 QThread::sleep() 是一个让你的线程休眠的API,如果你使用了它,那么你应该重新学习一下事件驱动设计。如果你将“多线程休眠”改为“多线程事件等待”(或者最好是不使用多线程),那么你将会节省巨大的系统资源,不然这些资源就被空转的线程浪费了。QThread::sleep() 用作计时器,也是一个糟糕的设计,因为从线程休眠到返回控制的这段时间,是很难受到控制的。理论上讲,当进程终止时,休眠的线程也会导致问题。前台线程可以阻止程序终止,直到他们被唤醒,而后台线程永远不会唤醒前台线程,结果就是程序的清理终止一直被阻止。
2. 禁止子线程操作GUI
Qt 的 GUI 操作不是线程安全的,所以非主线程外一切线程对GUI的操作都是不安全的。这就意味着窗口小部件、QtQuick、QPixmap,以及其他任何类的对象都不能在非主线程操作GUI。当然也有些特例:GUI函数只操作数据,不触及窗口UI管理,这是可以被子线程调用的,比如 QImage 和 QPainter。但是依旧要小心,像 QBitmap、 QPixmap 这些类依旧是线程不安全的。可以查询每个 API 的官方文档:如果你没有看到 可重入(reentrant) 标识,那么在子线程的一切调用都是不安全的。
3. 不要阻塞主线程
不要在主线程调用任何不明确阻塞时间的函数(比如 QThread::wait() ),因为这些函数会使你主线程的事件循环暂停,造成UI冻结。如果你的主线程阻塞时间足够长,那么操作系统会认为你的程序冻结,会询问你要不要干掉它(鲲哥: Windows常见于 “窗口未响应” )。不管是无限阻塞还是长时间阻塞主线程,对程序来说都是很搓的做法。
4. 一定要在拥有QObjects所有权的线程上对其进行销毁
Qt从设计上就不允许在非拥有QObject 所有权的线程上对其进行销毁,这意味这,一个QThread在销毁之前,它所掌握的所有QObject必须先销毁。如果没有这么做,那么很可能引发数据完整问题,比如常见的内存泄露和进程 crash 。
那么如何确定销毁QObject 的线程就是拥有其所有权的线程呢?我们可以将其创建为在QThread::run() 方法函数内执行的自动变量,关联QThread::finished() 信号与QThread::deleteLater() 槽函数;也可以通过moveToThread() 将QObject 移动到其他线程进行延迟析构。注意,一旦你把QObject从原来拥有它的线程移动到新线程后,你就不能用原来的线程操作它了,因为它的所有权属于新的线程了。
5. 线程同步的时候,不要相信自己的直觉
一种非常常见的设计方式就是一个线程把自己的运行状态,通过布尔变量的形式传递给监控线程,供其监控。一个简单的数据结构,一个线程写,另一个线程读,这样看起来是不需要做同步保护,因为我们能确保最终能读到它,是这样吗?直觉上看起来是没问题的,实际上就是这么简单一个案例也是线程不安全的。
C++标准规定线程同步是强制性的,任何超纲的行为都会导致未定义行为(UB)。如果你并没有做线程同步,即使是一个简单案例,也会给你制造问题。事实上,Linux内核发现的一些严重BUG就是这么产生的。我们最应该做的不是过度思考安全与否,假设有一个被多个线程同时操作的数据,即使这个数据看起来多么人畜无害,产生问题的几率有多么小,我们一定要用合适同步手段来保护这个数据。
6. 假设QObject是不可重入的
一个函数能被不同的线程不同数据同时访问,且不需要任何同步方法保护,那么这个函数就是可重入的。Qt官方文档显示QObject是可重入的,但实际上却有很多警告:
- 基于事件的类型是不可重入的(定时器、socket等等)
- 假设某一QObject正在其归属的线程中进行事件派发,如果你在另一个线程操作它,这就可能会导致竞争。
- 从属于同一父子关系树的所有QObject对象,必须在同一个线程进行处理。
- 删除QThread 对象之前,必须先把其拥有的所有QObject对象干掉。
- 你必须在一个对象归属的线程里面使用对这个对象使用 moveToThread() 。
为了避免以上所有特殊场景,最简单的方法就是认为QObject 是不可重入的。事实上,这就意味着你必须在QObject 所属的线程上操作它。这也会让你远离所有可能会出现问题,同时也是难以察觉的场景。
7. 避免往QThread增加槽函数
因为QThread 对象和创建他们的线程存在关联(或许不存在关联),但在子线程使用信号槽的时候,很容易产生问题。尽管子线程可以使用槽函数,当需要规避大量难以察觉的缺陷时,我们建议你不要这么做。
如果你不需要重写QThread::run() 函数,那么请不要从QThread 派生子类。只需要创建一个QThread 对象即可,这样你就能避免槽函数问题(更多请关注作者博客的其他博文)。
8. 请使用C++标准库,因为更好用
最后,不管是C++标准库,还是第三方库,在多线程方面都比Qt更具特色。它们包含Qt没有的功能——并行算法、协程、锁存器、内存屏障、原子智能指针、任务延续、executors、并发队列、分布式计数器等等。
Qt的多线程在一些场景下仍然好用,比如C++标准库里面没有线程池,但是Qt就有。好消息就是C++标准库和Qt完美兼容,你可以自由地将标准库引入你的代码里面。事实上,除非你要用线程操作QObject 对象才必须用到QThread,不然使用C++标准库还是Qt的多线程库,依赖于你个人喜好。
相关推荐
- 深度解密epoll 如何工作的?(epoll基本处理流程)
-
epoll...
- 大乐透第19082期:头奖开出7注1000万分落六地 奖池41亿元
-
2019年7月17日晚开奖的体彩超级大乐透第19082期开奖号码为:前区06、18、20、21、31,后区03、04。本期大乐透前区号码五区比为1:0:3:0:1,二区和四区号码没有给出。当期前区和值...
- 【开奖】4月27日周六:福彩、体彩(2021年4月27日体彩开奖结果)
-
4月27日开奖福彩3D第2019110期:61222选5第2019110期:0812202122排列3第19110期:303排列5第19110期:30305大乐透第19047期:0304...
- “红狒狒”落户哈尔滨铁路局(哈尔滨铁路红肠)
-
这几天,“红人”“红狒狒”在牡丹江机务段可引起了不小的轰动,众粉丝争相与其拍照留念,在该段人气爆棚!“红狒狒”到底何许人也?“红狒狒”,中文名:和谐3D型电力机车;绰号:红狒狒、番茄;制造商:大连机...
- 2D、3D、2.5D,做游戏还是搞噱头?玩家都晕了
-
前言游戏类型就像某种潮流,一种流行罢,另一种接棒成为主流。前两年的新作大多以“开放世界”为标签,在追求纯沙盒的过程中打造出一些细致的分类,比如说“类GTA沙盒”。诚然,纯碎的沙盒游戏并不多见,业内只有...
- 《战神4》PC版宣传片发布 GTX 1070即可60帧畅玩
-
在今年10月的时候索尼PlayStation官方正式宣布圣莫尼卡2018年的《战神4》将于2022年1月14日推出PC版本,官方在今天公布了一段PC版宣传片,并且公开了游戏的配置需求。下面让我们一起来...
- 男星深情好丈夫形象崩塌,半夜搂美女坐大腿,举止亲密
-
近日,于晓光被拍到深夜在酒吧玩,结束后与一名女子一起上车离开。上车后,女子直接坐在了他腿上,他也顺势搂着美女,美女满脸笑容地坐在他腿上玩手机离开。可能有人会好奇,于晓光是谁呢?于晓光是韩国艺人秋瓷炫的...
- d3d12dll丢失怎么修复?d3d12dll加载失败怎么解决?
-
d3d12.dll丢失怎么修复?d3d12.dll加载失败怎么解决?很多朋友想要运行游戏的时候都会遇到这个问题,这种情况该怎么办呢?今天系统之家小编给朋友们讲讲具体的解决方法,操作其实还蛮简单的。...
- 许多玩家反馈《生化4RE》PC一直崩溃 无法进入游戏
-
今日(3月24日),卡普空《生化危机4:重制版》正式发售,然而有部分PC玩家遇到了游戏崩溃等问题。很多玩家在贴吧发帖称游戏遇到了严重的崩溃问题,且经常反复,报错代码普遍为FatalD3Derror...
- 微软正式推出适用于WSL Linux的D3D12 GPU视频加速技术
-
今天,微软正式向WindowsSubsystemforLinux(WSL)用户发布了Direct3D12GPU视频加速支持。在微软通过WSL允许在Linux下使用Open...
- 《怪物猎人:崛起》曙光系统报错“Fatal d3d error”的解决办法
-
《怪物猎人:崛起》曙光系统报错“Fatald3derror”的解决办法不少小伙伴反应《怪物猎人:崛起》DLC曙光预载以后打不开游戏,出现了Fatald3derror类似的错误代码,这类问题的解...
- Mac+双屏,前端程序员的专业配置 - Loctek 乐歌 D3D 双屏电脑显示器支架
-
做FE也有一段日子了,电脑屏幕每天在设计稿、浏览器、IDE、即时通讯工具、Terminal、邮箱之间切换。虽然mac的工作区带来了很多灵活,但是依然略显不足。于是入手支架,把公司配的电脑和显示器发挥起...
- RPC 的原理和简单使用(rpc详解)
-
RPC的概念RPC,RemoteProcedureCall,翻译成中文就是远程过程调用,是一种进程间通信方式。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数。在调用的...
- 大厂开源的golang微服务rpc框架 — kitex
-
提前rpc估计所有的开发同学都知道,不知道的也无所谓,毕竟我也好几年没用了,今天带大家在复习一下。RPC(RemoteProcedureCall):远程过程调用,...
- 干货!一文掌握Protobuf所有语言所有用法,快收藏
-
说实话,Protobuf这个库,让人相见时难别亦难,东风无力百花残,每次等到要用它的时候,总感觉还没有完全掌握它的用法,而实际上等去百度或者谷歌的时候,教程都是多么的凌乱不堪。学会它,最直接关系到的,...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)