qt文件操作的一些技巧

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

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.mzph.cn/web/3655.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA MQTT 发布主题请求,订阅主题接收,订阅主题回复,发布主题再接收回复,三步走

先看效果 一、准备工作 1.官网下载emqx压缩包放到自己的盘符下&#xff0c;不要带中文路径 下载 EMQX 2.在路径的bin中&#xff0c;cmd&#xff0c;启动emqx服务 emqx start 3.访问服务&#xff0c;能打开就证明启动成功&#xff0c;登录的话官网默认的密码账号&#xff08;…

【C#】Stopwatch计时器

使用Stopwatch检查C#中代码块的执行时间&#xff0c;比如歌曲&#xff0c;图片的下载时间问题 首先&#xff0c;我们可看到Stopwatch 类内部的函数。 根据需求&#xff0c;我们具体可使用到 Start() 开始计时&#xff0c;Stop() 停止计时等 //创建 Stopwatch 实例 Stopwatch …

STM32单片机C语言模块化编程实战:LED控制详解与示例

一、开发环境 硬件&#xff1a;正点原子探索者 V3 STM32F407 开发板 单片机&#xff1a;STM32F407ZGT6 Keil版本&#xff1a;5.32 STM32CubeMX版本&#xff1a;6.9.2 STM32Cube MCU Packges版本&#xff1a;STM32F4 V1.27.1 之前介绍了很多关于点灯的方法&#xff0c;比如…

ARM DMIPS算力说明

ARM DMIPS算力说明 ARM算力参考官网地址 https://en.wikipedia.org/wiki/List_of_ARM_processors Product familyARM architectureProcessorFeatureCache (I / D), MMUTypical MIPS MHzReferenceARM1ARMv1ARM1First implementationNoneARM2ARMv2ARM2ARMv2 added the MUL (mu…

【SSM进阶学习系列丨整合篇】Spring+SpringMVC+MyBatis 框架配置详解

文章目录 一、环境准备1.1、创建数据库和表1.2、导入框架依赖的jar包1.3、修改Maven的编译版本1.4、完善Maven目录1.5、编写项目需要的包1.6、编写实体、Mapper、Service 二、配置MyBatis环境2.1、配置mybatis的主配置文件2.2、编写映射文件2.3、测试环境是否正确 三、配置Spri…

el-table 三角形提示

<template><div><el-table :data"tableData" style"width: 100%"><el-table-column prop"ddd" label"日期2" width"150" /><el-table-column prop"ddd" label"日期2" width…

[C++][算法基础]分组背包问题(动态规划)

有 &#x1d441; 组物品和一个容量是 &#x1d449; 的背包。 每组物品有若干个&#xff0c;同一组内的物品最多只能选一个。 每件物品的体积是 &#xff0c;价值是 &#xff0c;其中 &#x1d456; 是组号&#xff0c;&#x1d457; 是组内编号。 求解将哪些物品装入背包&a…

iOS CI/CD 持续集成 组件化专题二 Cocoapods /Cocoapods Packager 问题汇总

执行pod package xxx.podspec --force --no-mangle --exclude-deps --verbose 报错 warning: Building targets in manual order is deprecated - check "Parallelize build for command-line builds" in the project editor, or set DISABLE_MANUAL_TARGET_ORDER_BU…

Linux 静态IP地址修改与报错处理

目录 查看网卡信息 Ubuntu 16.04.5 LTS、Ubuntu 20.04.6 LTS Ubuntu 18.04.6 LTS Centos 8.0 修改IP地址 将IP地址置空 确认修改是否生效 查看网卡信息 ifconfig ifconfig -a Ubuntu 16.04.5 LTS、Ubuntu 20.04.6 LTS 修改配置 vim /etc/network/interfaces # This …

AI大模型探索之路-训练篇1:大语言模型微调基础认知

文章目录 前言一、微调技术概述二、微调的必要性三、大模型的微调方法四、微调过程中的技术细节五、微调后的模型评估与应用总结 前言 在人工智能的广阔研究领域内&#xff0c;大型预训练语言模型&#xff08;Large Language Models, LLMs&#xff09;已经成为推动技术革新的关…

一、路由基础

1.路由协议的优先级 路由器分别定义了外部优先级和内部优先级&#xff08;越小越优&#xff09; 路由选择顺序&#xff1a;外部优先级>>内部优先级&#xff08;相同时&#xff09; ①外部优先级&#xff1a;用户可以手工为各路由协议配置的优先级 ②内部优先级&#xf…

go开发环境安装配置(vscode)

安装 变量 $GOROOT 表示 Go 在你的电脑上的安装位置 $GOARCH 表示目标机器的处理器架构,它的值可以是 386、amd64 或 arm $GOOS 表示目标机器的操作系统,它的值可以是 darwin、freebsd、linux 或 windows $GOBIN 表示编译器和链接器的安装位置,默认是 $GOROOT/bin,Go 1.0.3可…

目标检测YOLO实战应用案例100讲-基于YOLOv5的目标检测与6D位姿估计算法研究(中)

目录 3.3 相机成像原理 3.3.1 坐标系的建立及关系 3.3.2 相机标定 3.3.3 相机畸变

天星金融普及个人养老金制度,共筑老龄友好型社会

在人口老龄化的浪潮中&#xff0c;我国正面临着日益严峻的养老挑战。据国家卫健委数据显示&#xff0c;预计到2035年&#xff0c;我国60岁及以上人口占总人口的比例将超过30%&#xff0c;构建老龄友好型社会已成为国家发展的重要任务。在这一背景下&#xff0c;个人养老金制度的…

OmniPlan Pro for Mac v4.8.0中文激活版 项目流程管理工具

OmniPlan Pro for Mac是一款功能强大的项目管理软件&#xff0c;它以其直观的用户界面和丰富的功能&#xff0c;帮助用户轻松管理各种复杂的项目。 OmniPlan Pro for Mac v4.8.0中文激活版 通过OmniPlan Pro&#xff0c;用户可以轻松创建任务&#xff0c;设置任务的开始和结束时…

Pulsar【部署 02】Pulsar可视化工具Manager安装使用

Pulsar Manager 是一个基于 web 的 GUI 管理和监视工具&#xff0c;可帮助管理员和用户管理和监视租户、命名空间、主题、订阅、代理、集群等&#xff0c;并支持对多个环境进行动态配置。 可视化工具Manager安装使用 1.Docker1.1 拉取镜像并启动1.2 设置用户名密码1.3 登录并添…

leetcode热题HOT 152. 乘积最大子数组

一、问题描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出数组中乘积最大的非空连续子数组&#xff08;该子数组中至少包含一个数字&#xff09;&#xff0c;并返回该子数组所对应的乘积。 测试用例的答案是一个 32-位 整数。 二、问题分析&#xff1a; 考虑到乘…

acwing算法提高之数据结构--并查集

目录 1 介绍2 训练3 参考 1 介绍 本专题用来记录并查集相关的题目。 并查集模板&#xff1a; //初始化 for (int i 1; i < n; i) { //n为结点数目p[i] i; }//查找 find(int x) {if (p[x] ! x) p[x] find(p[x]);return p[x]; }//合并 int pa find(a); int pb find(b)…

Java进阶知识点及案例总结(续2)

深浅拷贝 浅拷贝 开发中我们经常需要复制一个对象或数组&#xff0c;如果直接使用赋值&#xff0c;当改变复制之后的对象或数组时&#xff0c;原对向也会改变&#xff0c;拷贝时我们需要改变复制之后的对象或数组的值&#xff0c;但不改变原对象或数组的值。 浅拷贝和深拷贝…

openstack界面简单修改

openstack Ubuntu主题登录界面修改修改登陆界面背景登录框边缘添加透明效果修改登录界面logo更换站点图片更换项目logo图片 本实验基于VMware17&#xff0c;使用Ubuntu2310搭建openstack-B版 Ubuntu主题 以下配置只对Ubuntu主题生效 登录界面修改 原界面 关闭登录界面域名输…