如何:使用 PLINQ 循环访问文件目录
更新:2010 年 5 月
此示例演示对文件目录执行并行操作的两种简单方式。 第一个查询使用 GetFiles 方法填充目录以及所有子目录中文件名的数组。 此方法在填充整个数组之前不返回,因此,可能会在操作开始导致延迟。 但是,填充数组后,PLINQ 可以非常快地并行处理它。
第二个查询使用静态 EnumerateDirectories 和 EnumerateFiles 方法,这两个方法立即开始返回结果。 循环访问大型目录树时,此方法可能更快,但是相对于第一个示例处理时间可能取决于许多因素。
警告 |
---|
这些示例旨在演示用法,运行速度可能不如等效的顺序 LINQ to Objects 查询快。有关加速的更多信息,请参见了解 PLINQ 中的加速。 |
示例
下面的示例演示如何在下面这种简单的情况中循环访问文件目录:您有权访问树中的所有目录,文件大小不太大,并且访问时间不太多。 此方法在最开始构造文件名数组时有一段时间的延迟。
struct FileResult
{
public string Text;
public string FileName;
}
// Use Directory.GetFiles to get the source sequence of file names.
public static void FileIteration_1(string path)
{
var sw = Stopwatch.StartNew();
int count = 0;
string[] files = null;
try
{
files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine("You do not have permission to access one or more folders in this directory tree.");
return;
}
catch (FileNotFoundException)
{
Console.WriteLine("The specified directory {0} was not found.", path);
}
var fileContents = from file in files.AsParallel()
let extension = Path.GetExtension(file)
where extension == ".txt" || extension == ".htm"
let text = File.ReadAllText(file)
select new FileResult { Text = text , FileName = file }; //Or ReadAllBytes, ReadAllLines, etc.
try
{
foreach (var item in fileContents)
{
Console.WriteLine(Path.GetFileName(item.FileName) + ":" + item.Text.Length);
count++;
}
}
catch (AggregateException ae)
{
ae.Handle((ex) =>
{
if (ex is UnauthorizedAccessException)
{
Console.WriteLine(ex.Message);
return true;
}
return false;
});
}
Console.WriteLine("FileIteration_1 processed {0} files in {1} milliseconds", count, sw.ElapsedMilliseconds);
}
下面的示例演示如何在下面这种简单的情况中循环访问文件目录:您有权访问树中的所有目录,文件大小不太大,并且访问时间不太多。 此方法比前面的示例更快地开始生成结果。
struct FileResult
{
public string Text;
public string FileName;
}
// Use Directory.EnumerateDirectories and EnumerateFiles to get the source sequence of file names.
public static void FileIteration_2(string path) //225512 ms
{
var count = 0;
var sw = Stopwatch.StartNew();
var fileNames = from dir in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
select dir;
var fileContents = from file in fileNames.AsParallel() // Use AsOrdered to preserve source ordering
let extension = Path.GetExtension(file)
where extension == ".txt" || extension == ".htm"
let Text = File.ReadAllText(file)
select new { Text, FileName = file }; //Or ReadAllBytes, ReadAllLines, etc.
try
{
foreach (var item in fileContents)
{
Console.WriteLine(Path.GetFileName(item.FileName) + ":" + item.Text.Length);
count++;
}
}
catch (AggregateException ae)
{
ae.Handle((ex) =>
{
if (ex is UnauthorizedAccessException)
{
Console.WriteLine(ex.Message);
return true;
}
return false;
});
}
Console.WriteLine("FileIteration_2 processed {0} files in {1} milliseconds", count, sw.ElapsedMilliseconds);
}
使用 GetFiles 时,请确保您对树中的所有目录具有足够的权限。 否则,将引发一个异常并且不会返回任何结果。 在 PLINQ 查询中使用 EnumerateDirectories 时,以允许您继续循环访问的正常方式处理 I/O 异常存在问题。 如果您的代码必须处理 I/O 或未授权访问异常,则应考虑如何:使用并行类循环访问文件目录中介绍的方法。
如果 I/O 延迟成为问题,例如通过网络的文件 I/O,请考虑使用 TPL 和传统 .NET 异步编程和此博客文章中介绍的某种异步 I/O 技巧。
请参见
概念
修订记录
日期 |
修订记录 |
原因 |
---|---|---|
2010 年 5 月 |
添加了有关用法与 加速的注释。 |
客户反馈 |