并行中的枚举目录消耗非常高的物理内存使用量

问题描述 投票:0回答:2

我写了一个实用程序,它将搜索系统中的所有固定驱动器以查找某些扩展名的文件。一些驱动器包含数百万个文件夹(例如,3000万个),文件可以在不同的深度(例如,第6个/第7个子文件夹)找到。在下面找到我正在使用的功能,

private void ReadDirectories(string targetDirectory)
    {
        IEnumerable<string> files = Directory.EnumerateFiles(targetDirectory).AsParallel();
        ConcurrentBag<string> filesBag = new ConcurrentBag<string>(files);
        Parallel.ForEach(filesBag, (file) =>
       {
           Interlocked.Increment(ref totalFileCount);
           if (extension is a text/excel/word file )
           {
               try
               {
                   // Some logic here
               }
               catch (AggregateException Aex)
               {
                   Log("Aggregate exception thrown. " + Aex.Message + Aex.StackTrace + Aex.InnerException);
               }
               catch (Exception ex)
               {
                   Log("File read failed: " + file + ex.Message + ex.StackTrace + ex.InnerException);
                   return; // This is break equivalent in Parallel.ForEach

               }
           }

       });

        IEnumerable<string> directories = Directory.EnumerateDirectories(targetDirectory).AsParallel();
        ConcurrentBag<string> directoryBag = new ConcurrentBag<string>(directories);
        Parallel.ForEach(directoryBag, (subDirectory) =>
         {
             try
             {
                 ReadDirectories(subDirectory);
             }
             catch (AggregateException Aex)
             {
                 Log("Aggregate exception thrown. " + Aex.Message + Aex.StackTrace + Aex.InnerException);
             }
             catch (UnauthorizedAccessException Uaex)
             {
                 Log("Unauthorized exception: " + Uaex.Message + Uaex.StackTrace + Uaex.InnerException);
                 return;
             }
             catch (AccessViolationException Aex)
             {
                 Log("Access violation exception: " + Aex.Message + Aex.StackTrace + Aex.InnerException);
                 return;
             }
             catch (Exception ex)
             {
                 Log("Error while reading directories and files : " + ex.Message + ex.StackTrace + ex.InnerException);
                 return;
             }
         });

    }

我面临的问题是,一旦应用程序开始枚举文件夹,物理内存消耗越来越多,并在一段时间后达到峰值(99%)。此时,不能执行任何其他活动。但我的应用程序内存大约在80 -90 MB之间运行。想知道物理内存使用率如此之高的原因,代码有问题吗?

c# io task-parallel-library ienumerable
2个回答
1
投票

考虑你的数字:3000万个文件夹,每个文件夹可能有一些文件留给你一些像文件和目录名称的1亿个字符串。并且由于该方法是递归的,所以袋子都保持到递归结束。

因此,如果平均文件/目录名称长度为100个字符,那么只有名称的RAM高达10GB。


0
投票

正如其他人所解释的那样,存储这么多字符串会耗费大量内存而无法扩展。尝试并行枚举文件夹和文件也不会加快处理速度。

使用Directory.EnumerateFiles甚至更好,DirectoryInfo.EnumerateFilesSearchOption.AllDirectories更快地枚举当前文件夹和子文件夹中的所有文件并并行处理文件。

一个快速而又脏的选项是使用LINQ查询来过滤所有目标文件,使用Parallel.ForEach来处理文件,例如:

var extensions=new[]{".docx", ".xlsx",...};
var folder=new DirectoryInfo(targetDirectory);
var files=from file in folder.EnumerateFiles("*.*", SearchOption.AllDirectories)
          where extensions.Contains(file.Extension,StringComparer.InvariantCultureIgnoreCase)
          select file;

Parallel.ForEach(files,file=>ProcessFile(file));

这将使用与机器中的核心一样多的任务来处理文件。您可以通过指定不同的MaxDegreeOfParallelism选项来使用更多任务:

var options=new ParallelOptions { MaxDegreeOfParallelism = 4 }
Parallel.ForEach(files,options,ProcessFile);

Parallel.ForEach将根据需要从files查询中提取文件名。一旦EnumerateFiles返回第一个结果,它就会开始处理,而不是等待所有文件名加载并缓存在内存中。

© www.soinside.com 2019 - 2024. All rights reserved.