qt文件操作的一些技巧

news/2024/5/4 0:19:21

二、多线程处理大文件的最佳实践

1、避免共享状态

最根本解决方案是避免多个线程访问同一资源。我们可以将大文件分割成多个独立部分,分别由不同线程独占处理。只需引入一些同步点,进行简单的合并即可。


2、合理利用互斥量和读写锁

如果无法避免共享资源,就必须使用同步原语如QMutex和QReadWriteLock来保护临界区。这能够确保同一时间只有一个线程能访问资源。但注意锁粒度不宜过大,会影响并发性能。


3、使用无锁原子操作

Qt提供了QAtomicInteger等原子操作类,我们可以用它们来保护一些简单的计数、状态位等共享变量,避免加锁开销。


4、避免死锁陷阱

死锁通常由多线程循环等待造成。解决办法有:统一加锁顺序、使用定时锁、使用更精细的同步原语、避免嵌套加锁等。


5、使用QWaitCondition减少忙等

当线程需要等待某个条件时,可使用QWaitCondition挂起线程,从而避免忙等浪费资源。


6、限制最大线程数量

如果检测到线程数过高,可拒绝创建新线程,减小潜在的竞争窗口。


7、及时关闭文件描述符

尽快关闭已不需要的打开文件,可以减少并发冲突的可能性。

聪明的您一定已经发现,很多最佳实践都不限于文件操作,而是通用的多线程编程原则。这些原则确实很重要,但并不直接解决我们的问题。现在,让我们来展示一些Qt提供的专门技巧和类,用于高效、安全地在多线程环境中处理大型二进制文件。


三、多线程处理大文件的八大技巧

给力技巧一:借助QFile原生锁功能

QFile类自身就提供了锁定功能,可以用来控制多线程对文件的并发访问。比如lock()和unlock()函数可以实施互斥锁:

QFile file("huge.bin");
file.open(QIODevice::ReadWrite);
file.lock(); // 加锁
// 访问文件内容
file.unlock(); // 解锁 
file.close();

这种做法虽然简单可靠,但显然会影响并发性能,因为无法利用多核优势。所以它更适用于读写操作不在关键路径上的场合。


给力技巧二:QSaveFile让写文件时无忧

当多个线程并发写入同一个文件时,很容易出现文件损坏。为了避免这一问题,我们可以使用Qt提供的QSaveFile辅助类。它会在真正写文件之前,先创建一个临时文件进行操作,数据写入完成后再执行系统级的原子重命名操作。

QSaveFile file("data.bin");
file.open(QIODevice::WriteOnly);
// 写入数据
file.commit(); // 原子化提交数据

使用QSaveFile可以确保多个线程写入同一文件时,产生的要么是完整的新文件,要么是完整的旧文件,从不会出现中间状态或文件损坏。它为大文件的并发写操作提供了有力保障。


给力技巧三:QMutex组合拳

如果对并行性要求比较高,我们可以自行结合使用QMutex等同步原语。比如为每个QFile实例分配一个互斥量,来确保其读写的原子性:

QMutex fileMutex;
void processFile() {fileMutex.lock();QFile file("huge.bin"); file.open(QIODevice::ReadWrite);// 读写操作file.close();fileMutex.unlock();
}

注意要控制好互斥量的锁范围。如果粒度过大,并发度会降低。反之亦然,锁的范围太小可能无法很好地保证数据完整性。大家需要在这两个极端之间权衡取舍。


给力技巧四:QDataStream高手进阶

QDataStream专门为QIODevice设计的数据流类,它支持在二进制文件中定位和读写各种Qt元数据类型,还能自动处理字节序等问题。我们可以利用它提高大文件并发操作的性能:

QFile file("huge.bin");
file.open(QIODevice::ReadWrite);
QDataStream stream(&file);

// 跳到特定位置
stream.device()->seek(offset);
// 读写数据
stream >> someData >> moreData;
stream << newData;

多线程下并发读写是安全的,只要各线程操作不同的文件区域。结合QMutex等同步手段,我们就能充分利用QDataStream的优势,让多线程大文件处理事半功倍。


给力技巧五:第三方高性能库

虽然Qt内建的文件处理能力已经相当强大,但对于一些特殊场景,我们可能需要借助一些功能更加专门的第三方库,以获得更高的性能和可靠性。

比如像LMDB、RocksDB这些由C++直接开发的嵌入式键值数据库,就针对高并发的大型二进制数据操作做了大量优化,其性能和稳定性通常会优于Qt自带的方案。如果您的项目对文件读写性能和并发访问安全性有很高要求,不妨考虑将它们与Qt进行整合。

此外,像mmap、FileMapper等内存映射库,也可以在特定场景下为Qt文件操作加速。需要根据实际需求择优使用。


给力技巧六:Lock-Free设计

如果场景允许,我们还可以尝试完全规避锁操作,使用无锁(Lock-Free)队列等数据结构进行并发编程。相比于基于锁的传统方案,Lock-Free设计虽然对算法复杂度和内存管理要求较高,但是能够极大地降低并发开销,提高系统的整体吞吐量。

不过需要注意,Lock-Free程序的正确性比加锁方案更加脆弱,编写的难度也很大,容易出现细微的逻辑错误。所以只有对多线程编程有较高造诣并对系统有深入的了解时,才适合考虑这种方案。


给力技巧七:使用QThreadPool

Qt的QThreadPool类提供了跨平台的线程池管理,能够高效重用和调度线程资源,在处理大量小任务时表现出色。我们可以考虑将大文件分割为多个数据块,并通过QThreadPool并行处理:

QVector<QByteArray> datas = separateFile("huge.bin", numThreads);
QVector<QFuture<void>> futures;
for (int i = 0; i < numThreads; ++i) {futures.append(QtConcurrent::run(&QThreadPool::globalInstance(), processData, datas[i]));
}
//等待所有数据块处理完成
for (QFuture<void> &f : qAsConst(futures)) {f.waitForFinished();
}

QThreadPool内部会自动管理线程生命周期、维护活动线程数目等,极大简化了我们的编程工作。当然,过多的线程切换也可能引入不少开销,需要合理评估使用场景。

给力技巧八:使用QFile::map

我们前面讨论过,如果文件可以完全装入内存,就可以使用QFile的map函数将其映射到进程地址空间,避免频繁的IO操作。但映射操作需要较大的虚拟地址空间,内存开销也不小。

QFile file("huge.bin");
file.open(QIODevice::ReadOnly);
uchar* mappedData = file.map(0, file.size());
if (mappedData) {// 使用mappedData访问文件内容file.unmap(mappedData);
}
file.close();

所以在多线程环境下,我们可以考虑由主线程执行映射,然后派生出多个工作线程,在工作线程中并行访问映射的内存区域。这种做法虽然无法完全规避并发问题,但已经大大降低了潜在风险。


http://www.mrgr.cn/p/83457345

相关文章

jmeter 指定QPS压测接口

文章目录 jmeter 指定QPS压测接口更换语言为中文创建测试任务新建线程组右键线程组&#xff0c;新建http request&#xff0c;填写要你要压测的接口地址、参数如果需要自定义请求头&#xff0c;添加一个Http头信息管理器要查看结果和QPS统计数据&#xff0c;给上门的http请求添…

牛客NC162 二叉树中和为某一值的路径(三)【中等 dfs C++、Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/965fef32cae14a17a8e86c76ffe3131f 思路 既然要找所有路径上节点和等于目标值的路径个数&#xff0c;那我们肯定先找这样的路径起点啊&#xff0c; 但是我们不知道起点究竟在哪里&#xff0c; 而且任意节点都有…

项目大集成

一 keeplived 高可用 192.168.11.11nginx keeplived192.168.11.12nginx keeplived 两台均编译安装服务器 1 主服务器修改文件&#xff1a; 2 备服务器修改文本 scp keepalived.conf 192.168.11.12:/etc/keepalived/ 3 给主服务器添加虚拟ip ifconfig ens33:0 192.168…

机器学习-10-基于paddle实现神经网络

文章目录 总结参考本门课程的目标机器学习定义第一步&#xff1a;数据准备第二步&#xff1a;定义网络第三步&#xff1a;训练网络第四步&#xff1a;测试训练好的网络 总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍基于paddle实现神经网络。 参考 MNIST 训练_副…

Linux:进程状态

Linux&#xff1a;进程状态 进程状态运行状态R状态 阻塞状态S状态D状态T状态t状态 挂起状态僵尸进程 & 孤儿进程X状态Z状态孤儿进程 进程状态 当一个可执行程序&#xff0c;被载入内存&#xff0c;获得自己的PCB&#xff0c;那么其就可以变成一个进程。也许你学习过一些进…

java高校办公室行政事务管理系统设计与实现(springboot+mysql源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的闲一品交易平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于mvc的高校办公室行政…

AJAX——Promise-链式调用

1.Promise链式调用 概念&#xff1a;依靠then()方法会返回一个新生成的Promise对象特性&#xff0c;继续串联下一环任务&#xff0c;知道结束 细节&#xff1a;then()回调函数中的返回值&#xff0c;会影响新生成的Promise对象最终状态和结果 好处&#xff1a;通过链式调用&…

【Java基础】23.接口

文章目录 一、接口的概念1.接口介绍2.接口与类相似点3.接口与类的区别4.接口特性5.抽象类和接口的区别 二、接口的声明三、接口的实现四、接口的继承五、接口的多继承六、标记接口 一、接口的概念 1.接口介绍 接口&#xff08;英文&#xff1a;Interface&#xff09;&#xf…

鸿蒙OpenHarmony【小型系统编写“Hello World”程序】 (基于Hi3516开发板)

编写“Hello World”程序 下方将展示如何在单板上运行第一个应用程序&#xff0c;其中包括新建应用程序、编译、烧写、运行等步骤&#xff0c;最终输出“Hello World&#xff01;”。 前提条件 已参考[创建工程并获取源码]&#xff0c;创建Hi3516开发板的源码工程。 鸿蒙开发…

如何处理Keil uVision5注释无法输入汉字且输入汉字变成问号的问题

好久没用KEIL&#xff0c;今天在注释中出现无法输入汉字的情况&#xff0c;且输入或粘贴的汉字都变成了问号&#xff0c;解决方法很简单&#xff0c;将General Editor Settings: Encoding:设置为Chinese GB2312(Simplified)即可&#xff08;出现问号的当前设置是Encode in ANSI…

海外云手机怎么解决tiktok运营难题?

最近打算做TikTok的商家越来越多了&#xff0c;而做TikTok的第一步就面临如何养号、涨粉的困境&#xff0c;本文将介绍如何通过海外云手机轻松解决这些问题。 早期大家用的比较多的&#xff0c;是真机科学上网的方法。但是这种方法&#xff0c;需要自己搭建海外环境&#xff0c…

day04 51单片机-矩阵按键

1 矩阵按键 1.1 需求描述 本案例实现以下功能&#xff1a;按下矩阵按键SW5到SW20&#xff0c;数码管会显示对应的按键编号。 1.2 硬件设计 1.2.1 硬件原理图 1.2.2 矩阵按键原理 1.3软件设计 1&#xff09;Int_MatrixKeyboard.h 在项目的Int目录下创建Int_MatrixKeyboard…

【创建型模式】单例模式

一、单例模式概述 单例模式的定义&#xff1a;又叫单件模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。&#xff08;对象创建型&#xff09; 要点&#xff1a; 1.某个类只能有一个实例&#xff1b;2.必须自行创建这个实例&#xff1b;3.必须自行向整…

探索RadSystems:低代码开发的新选择(二)

系列文章目录 探索RadSystems&#xff1a;低代码开发的新选择&#xff08;一&#xff09;&#x1f6aa; 文章目录 系列文章目录前言一、RadSystems Studio是什么&#xff1f;二、用户认证三、系统角色许可四、用户记录管理五、时间戳记录总结 前言 在数字化时代&#xff0c;低…

HarmonyOs开发:导航tabs组件封装与使用

前言 主页的底部导航以及页面顶部的切换导航&#xff0c;无论哪个系统&#xff0c;哪个App&#xff0c;都是最常见的功能之一&#xff0c;虽然说在鸿蒙中有现成的组件tabs可以很快速的实现&#xff0c;但是在使用的时候&#xff0c;依然有几个潜在的问题存在&#xff0c;第一&a…

CountDownLatch倒计时器源码解读与使用

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. CountDownLatch有什么用 3. CountDownLatch底层原理 3.1. count…

不如你把我杀了吧 | 绘制自定义的 3D 地图

如何根据自己的json数据绘制类似这种地图,仅供参考 1、准备数据。 因为自定义,所以全部的数据都来源自己。我们需要准备地图数据(包括但不限于地图轮廓数据,点数据) 这里我的数据使用的是arcgis导出json数据,因此数据格式足够规范,这省去了很多的麻烦。 2、导入相关库、…

【智能算法】寄生捕食算法(PPA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2020年&#xff0c;AAA Mohamed等人受到自然界乌鸦-布谷鸟-猫寄生系统启发&#xff0c;提出了寄生捕食算法&#xff08;Parasitism – Predation Algorithm, PPA&#xff09;。 2.算法原理 2.1算法…

面向对象设计与分析40讲(25)中介模式、代理模式、门面模式、桥接模式、适配器模式

文章目录 门面模式代理模式中介模式 之所以把这几个模式放到一起写&#xff0c;是因为它们的界限比较模糊&#xff0c;结构上没有明显的差别&#xff0c;差别只是语义上。 这几种模式在结构上都类似&#xff1a; 代理将原本A–>C的直接调用变成&#xff1a; A–>B–>…

针对窗口数量多导致窗口大小显示受限制的问题,使用滚动条控制窗口

建议&#xff1a;首先观察结果展示&#xff0c;判断是否可以满足你的需求。 目录 1. 问题分析 2. 解决方案 2.1 界面设计 2.2 生成代码 2.3 源码实现 3. 结果展示 1. 问题分析 项目需要显示的窗口数量颇多&#xff0c;主界面中&#xff0c;如果一次性显示全部窗口&#x…