我写了一个实用程序,可以在系统中的所有固定驱动器中搜索某些扩展名的文件。一些驱动器包含数百万个文件夹(例如,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。想知道物理内存使用率如此之高的原因,代码是否有问题?
答案 0 :(得分:1)
考虑一下您的数量:3000万个文件夹,每个文件夹可能包含几个文件,这给您留下了大约1亿个文件和目录名称的字符串。并且由于该方法是递归的,因此所有袋子都保留到递归结束为止。
因此,文件/目录的平均名称长度为100个字符,仅这些名称最多可使用10GB的RAM。
答案 1 :(得分:0)
正如其他人所解释的那样,存储这么多字符串将耗尽很多内存,并且无法扩展。尝试枚举 parallel 中的文件夹和文件也不会加快处理速度。
使用Directory.EnumerateFiles甚至更好,将DirectoryInfo.EnumerateFiles与SearchOption.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
返回第一个结果后,它将立即开始处理,而不是等待所有文件名都加载并缓存在内存中。