目录

介绍

例子

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进行批处理相关推荐

  1. 使用WinAPI替代System.IO.Directory

    目录 介绍 使用代码 代码如何运作 下载源代码 - 22.6 KB 介绍 最近,我正在做一个需要读取Windows目录内容的项目,所以我使用了.NET提供的System.IO.Directory类中的 ...

  2. 【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 ...

  3. active directory教程入门

    转贴自http://gnaw0725.blogbus.com/logs/17762337.html#cmt active directory教程入门.对于刚接触活动目录的朋友们来说, 寻找一份适合ac ...

  4. TinyFrame升级之八:实现简易插件化开发

    本章主要讲解如何为框架新增插件化开发功能. 在.net 4.0中,我们可以在Application开始之前,通过PreApplicationStartMethod方法加载所需要的任何东西.那么今天我们 ...

  5. C# Winform下一个热插拔的MIS/MRP/ERP框架14(自动更新)

    对于软件来说,启用自动更新是非常必要的. 根据软件的应用场景,我们可以设计不同的更新模型. 目前,IMES框架运行在.Net framework 4.0下面,使用的Win系统版本在Win7,域内管控, ...

  6. 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线...

    重温.NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现 ...

  7. 迭代器模式和组合模式混用

    迭代器模式和组合模式混用 前言 园子里说设计模式的文章算得上是海量了,所以本篇文章所用到的迭代器设计模式和组合模式不提供原理解析,有兴趣的朋友可以到一些前辈的设计模式文章上学学,很多很有意思的.在He ...

  8. Asp.Net Web Api 2 实现多文件打包并下载文件示例源码

    前言 最近由于工作和个人事务,站点也好久没更新了,但这并不影响我对.NET的热情.站点的更新工作还是得想办法抽时间来完成的. 提要 今天利用中午的时间来写一篇关于Asp.Net Web Api下载文件 ...

  9. C# 系统应用之获取Windows最近使用记录

    由于毕业设计项目需要删除Windows最近历史记录,这就需要获取Windows最近历史记录 Recent.本文就主要叙述通过C#实现获取Recent中使用的文件和文件夹.首先声明该文章主要是结合自己的 ...

最新文章

  1. 利用QT实现X轴为时间动态显示曲线
  2. 300plc与组态王mpi通讯_MPI(DP)-ETH以太网转换器使用手册
  3. [leetcode]139. 单词拆分
  4. 接口jdk1.8与jdk1.9新特性
  5. Linux常用命令介绍(三)——基础操作命令
  6. 经济下行,薅点羊毛吧!
  7. 可用性测试启发式评估十条原则介绍
  8. vant实现三级联动
  9. QR 二维码纠错码(三)
  10. 解决notepad++ php代码美化
  11. 数理统计中常用函数、概率分布函数总结
  12. 360浏览器小号多开使用
  13. CloudNative:云原生(分布式云)的简介(发展演变/为什么需要/优势价值/安全/对比传统企业应用)、四大核心技术、CNCF云原生交互景观、云原生技术的使用经验及方法之详细攻略
  14. 面向对象的讨论-2022年5月4日
  15. dotnetfx35.exe
  16. ArcGIS的地理坐标系、大地坐标系
  17. 二维码的扫描和生成二维码
  18. 程序员知识产权问题:程序员在家自己开发小软件,公司是否有权利强制留下?
  19. 我们到底该不该去初创公司上班?
  20. python:读取Excel文件

热门文章

  1. visual studio installer可以卸载吗_技术帖 | 这些宝藏软件你安装了吗?
  2. 电脑java语言有什么用_Java语言是什么?_Java语言有什么优点
  3. 设计灵感|如何在海报设计中正确使用双色调风格?
  4. 中的listeners_C++中Future和Promise的一种简单实现
  5. cpp 编译dll_GCC编译基础
  6. 5月份----要做的
  7. Madagascar的宏定义函数--取最值、取整
  8. Linux容器:cgroup,namespace原理与实现
  9. 使用libbpf-bootstrap构建BPF应用程序
  10. 内存分配器ptmalloc,jemalloc,tcmalloc调研与对比