qt文件操作的一些技巧

news/2024/11/30 1:54:23/

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

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.ppmy.cn/news/1431262.html

相关文章

HarmonyOS ArkUI实战开发-页面跳转(Router、Ability)

页面跳转可以分为页面内跳转和页面间跳转&#xff0c;页面内跳转是指所跳转的页面在同一个 Ability 内部&#xff0c;它们之间的跳转可以使用 Router 或者 Navigator 的方式&#xff1b;页面间跳转是指所跳转的页面属与不同的 Ability &#xff0c;这种跳转需要借助 featureAbi…

linux系统安全与应用【下】

目录 1.开关机安全控制 1.1GRUB限制 2.终端登录安全控制 2.1 限制root只在安全终端登录 2.2 禁止普通用户登录 3.弱口令检测 3.1 Joth the Ripper&#xff08;JR&#xff09; 4.网络端口扫描 4.1 nmap命令 1.开关机安全控制 1.1GRUB限制 通常情况下在系统开机进入GRU…

工业级路由器如何异地组网及其作用

在传统行业中&#xff0c;随着技术的不断进步和数字化转型的加速&#xff0c;异地组网成为了提升工作效率、降低运营成本、增强企业灵活性的重要手段。工业级路由器作为实现异地组网的关键设备&#xff0c;其在企业网络架构中发挥着不可替代的作用。本文将详细解释工业级路由器…

我在本地部署通义千问Qwen1.5大模型,并实现简单的对话和RAG

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总…

STM32自动光控窗帘程序+Proteus仿真图 H桥L298驱动电机

目录 1、前言 2、仿真图 3、源程序 资料下载地址&#xff1a;STM32自动光控窗帘程序Proteus仿真图 H桥L298驱动电机 1、前言 基于STM32F103设计的智能光控窗帘&#xff0c;包含STM32芯片、光敏电阻、LCD1602显示屏、电机驱动控制模块等。 备注&#xff1a;通过ARM内部的…

ESP32与SD卡交互实现:文件读写实战与初始化详解及引脚定义

本代码实现ESP32与SD卡的交互&#xff0c;包括定义SPI引脚、创建自定义SPI类实例、编写WriteFile与ReadFile函数进行文件读写。setup函数初始化串口、SPI、SD卡&#xff0c;向“/test.txt”写入“myfirstmessage”&#xff0c;读取并打印其内容。loop函数留空待扩展。 1. 需要…

小游戏:贪吃蛇

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;贪吃蛇 &#x1f337;追光的人&#xff0c;终会万丈光芒 目录 &#x1f3dd;1.头文件&#xff1a; &#x1f3dd;2.实现文件&#xff1a; &#x1f3dd;3.测试文件 &#xff1a; 前言&#…

单片机 VS 嵌入式LInux (学习方法)

linux 嵌入式开发岗位需要掌握Linux的主要原因之一是&#xff0c;许多嵌入式系统正在向更复杂、更功能丰富的方向发展&#xff0c;需要更强大的操作系统支持。而Linux作为开源、稳定且灵活的操作系统&#xff0c;已经成为许多嵌入式系统的首选。以下是为什么嵌入式开发岗位通常…