使用Directory.EnumerateFiles进行批处理
目录
介绍
例子
Directory.EnumerateFiles来救援
评估
关于批处理的几句话
关于返回类型的几句话
结论
介绍
如果想要从目录中检索文件,对于大多数情况来说,Directory.GetFiles是一个简单的答案。但是,当您处理大量数据时,您可能需要更高级的技术。
例子
假设您有一个大数据解决方案,您需要处理一个包含200000个文件的目录。对于每个文件,您提取一些基本信息。
public record FileProcessingDto
{public string FullPath { get; set; }public long Size { get; set; }public string FileNameWithoutExtension { get; set; }public string Hash { get; internal set; }
}
请注意我们如何方便地在此处为DTO使用新的C# 9记录类型。
之后,我们发送提取的信息以供进一步处理。让我们用下面的代码片段来模拟它。
public class FileProcessingService
{public Task Process(IReadOnlyCollection<FileProcessingDto> files, CancellationToken cancellationToken = default){files.Select(p =>{Console.WriteLine($"Processing {p.FileNameWithoutExtension} located at {p.FullPath} of size {p.Size} bytes");return p;});return Task.Delay(TimeSpan.FromMilliseconds(20), cancellationToken);}
}
现在最后一部分是提取信息并调用服务。
public class Worker
{public const string Path = @"path to 200k files";private readonly FileProcessingService _processingService;public Worker(){_processingService = new FileProcessingService();}private string CalculateHash(string file){using (var md5Instance = MD5.Create()){using (var stream = File.OpenRead(file)){var hashResult = md5Instance.ComputeHash(stream);return BitConverter.ToString(hashResult).Replace("-", "", StringComparison.OrdinalIgnoreCase).ToLowerInvariant();}}}private FileProcessingDto MapToDto(string file){var fileInfo = new FileInfo(file);return new FileProcessingDto(){FullPath = file,Size = fileInfo.Length,FileNameWithoutExtension = fileInfo.Name,Hash = CalculateHash(file)};}public Task DoWork(){var files = Directory.GetFiles(Path).Select(p => MapToDto(p)).ToList();return _processingService.Process(files);}
}
请注意,在这里,我们以一种天真的方式行事,并通过Directory.GetFiles(Path)一次提取提取所有文件。
但是,一旦您通过以下方式运行此代码:
await new Worker().DoWork()
您会注意到结果远不能令人满意,并且应用程序正在大量消耗内存。
Directory.EnumerateFiles来救援
Directory.EnumerateFiles的作用是它返回IEnumerable<string>,从而允许我们一个一个地获取集合项。这反过来又可以防止我们在一次加载大量数据时过度使用内存。
尽管如此,正如您可能已经注意到的那样, FileProcessingService.Process包含延迟编码(我们使用简单延迟模拟的某种I/O操作)。在实际场景中,这可能是对外部 HTTP端点的调用或使用存储。这使我们得出结论,调用FileProcessingService.Process 200 000次可能效率低下。这就是为什么我们要一次性将合理批次的数据加载到内存中。
重新编写的代码如下所示:
public class WorkerImproved
{//omitted for brevitypublic async Task DoWork(){const int batchSize = 10000;var files = Directory.EnumerateFiles(Path);var count = 0;var filesToProcess = new List<FileProcessingDto>(batchSize);foreach (var file in files){count++;filesToProcess.Add(MapToDto(file));if (count == batchSize){await _processingService.Process(filesToProcess);count = 0;filesToProcess.Clear();}}if (filesToProcess.Any()){await _processingService.Process(filesToProcess);}}
}
在这里,我们使用foreach枚举集合,一旦达到批处理的大小,我们就会处理它并刷新集合。这里唯一有趣的时刻是在我们退出循环后最后一次调用service以刷新剩余的项目。
评估
Benchmark.NET产生的结果非常有说服力:
关于批处理的几句话
在本文中,我们浏览了软件工程中的常见模式。合理数量的批处理有助于我们克服以逐项方式工作的I/O损失和一次性将所有项目加载到内存中的过多内存消耗。
通常,在对多个项目进行I/O操作时,您应该努力使用批处理API。一旦项目数量变多,您应该考虑将这些项目分成批次。
关于返回类型的几句话
在处理代码库时,我经常看到类似于以下内容的代码:
public IEnumerable<int> Numbers => new List<int> { 1, 2, 3 };
我认为这段代码违反了Postel的原则,随之而来的事情是,作为一个属性的消费者,我无法弄清楚我是否可以一个一个地枚举项目,或者它们是否只是一次加载到内存中。
这是我建议对返回类型更具体的原因,即:
public IList<int> Numbers => new List<int> { 1, 2, 3 };
结论
批处理是一种很好的技术,可以让您优雅地处理大量数据。Directory.EnumerateFiles是允许您为包含大量文件的目录组织批处理的API。
https://www.codeproject.com/Tips/5298439/Batch-Processing-with-Directory-EnumerateFiles
使用Directory.EnumerateFiles进行批处理相关推荐
- 使用WinAPI替代System.IO.Directory
目录 介绍 使用代码 代码如何运作 下载源代码 - 22.6 KB 介绍 最近,我正在做一个需要读取Windows目录内容的项目,所以我使用了.NET提供的System.IO.Directory类中的 ...
- 【C#进阶四】详细总结C#中的文件和I/O流之文件和目录(File 、 FileInfo、Directory、DirectoryInfo和Path)
文章目录 1 Flie类 1.1常用属性.方法 1.2 代码示例(详细) 2 FileInfo 2.1 常用属性.方法 2.2 代码示例 3 Directory 类 3.1 常用方法和属性: 3.2 ...
- active directory教程入门
转贴自http://gnaw0725.blogbus.com/logs/17762337.html#cmt active directory教程入门.对于刚接触活动目录的朋友们来说, 寻找一份适合ac ...
- TinyFrame升级之八:实现简易插件化开发
本章主要讲解如何为框架新增插件化开发功能. 在.net 4.0中,我们可以在Application开始之前,通过PreApplicationStartMethod方法加载所需要的任何东西.那么今天我们 ...
- C# Winform下一个热插拔的MIS/MRP/ERP框架14(自动更新)
对于软件来说,启用自动更新是非常必要的. 根据软件的应用场景,我们可以设计不同的更新模型. 目前,IMES框架运行在.Net framework 4.0下面,使用的Win系统版本在Win7,域内管控, ...
- 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线...
重温.NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现 ...
- 迭代器模式和组合模式混用
迭代器模式和组合模式混用 前言 园子里说设计模式的文章算得上是海量了,所以本篇文章所用到的迭代器设计模式和组合模式不提供原理解析,有兴趣的朋友可以到一些前辈的设计模式文章上学学,很多很有意思的.在He ...
- Asp.Net Web Api 2 实现多文件打包并下载文件示例源码
前言 最近由于工作和个人事务,站点也好久没更新了,但这并不影响我对.NET的热情.站点的更新工作还是得想办法抽时间来完成的. 提要 今天利用中午的时间来写一篇关于Asp.Net Web Api下载文件 ...
- C# 系统应用之获取Windows最近使用记录
由于毕业设计项目需要删除Windows最近历史记录,这就需要获取Windows最近历史记录 Recent.本文就主要叙述通过C#实现获取Recent中使用的文件和文件夹.首先声明该文章主要是结合自己的 ...
最新文章
- 利用QT实现X轴为时间动态显示曲线
- 300plc与组态王mpi通讯_MPI(DP)-ETH以太网转换器使用手册
- [leetcode]139. 单词拆分
- 接口jdk1.8与jdk1.9新特性
- Linux常用命令介绍(三)——基础操作命令
- 经济下行,薅点羊毛吧!
- 可用性测试启发式评估十条原则介绍
- vant实现三级联动
- QR 二维码纠错码(三)
- 解决notepad++ php代码美化
- 数理统计中常用函数、概率分布函数总结
- 360浏览器小号多开使用
- CloudNative:云原生(分布式云)的简介(发展演变/为什么需要/优势价值/安全/对比传统企业应用)、四大核心技术、CNCF云原生交互景观、云原生技术的使用经验及方法之详细攻略
- 面向对象的讨论-2022年5月4日
- dotnetfx35.exe
- ArcGIS的地理坐标系、大地坐标系
- 二维码的扫描和生成二维码
- 程序员知识产权问题:程序员在家自己开发小软件,公司是否有权利强制留下?
- 我们到底该不该去初创公司上班?
- python:读取Excel文件
热门文章
- visual studio installer可以卸载吗_技术帖 | 这些宝藏软件你安装了吗?
- 电脑java语言有什么用_Java语言是什么?_Java语言有什么优点
- 设计灵感|如何在海报设计中正确使用双色调风格?
- 中的listeners_C++中Future和Promise的一种简单实现
- cpp 编译dll_GCC编译基础
- 5月份----要做的
- Madagascar的宏定义函数--取最值、取整
- Linux容器:cgroup,namespace原理与实现
- 使用libbpf-bootstrap构建BPF应用程序
- 内存分配器ptmalloc,jemalloc,tcmalloc调研与对比