訓練
如何:使用 PLINQ 逐一查看檔案目錄
本文示範兩個方式來平行處理檔案目錄的作業。 第一個查詢使用 GetFiles 方法來填入目錄和所有子目錄中的檔案名稱陣列。 這個方法可能會在作業一開始即造成延遲,因為其在整個陣列填入之前並不會傳回。 不過,在填入陣列之後,PLINQ 可以平行方式快速地加以處理。
第二個查詢使用靜態 EnumerateDirectories 和 EnumerateFiles 方法,會立即開始傳回結果。 逐一查看大型的目錄樹狀時,這種方法的執行速度更快,但相較於第一個範例,處理時間會取決於許多因素。
注意
這些範例是為了示範用法,執行速度可能比不上對應的循序 LINQ to Objects 查詢。 如需加速的詳細資訊,請參閱認識 PLINQ 中的加速。
本範例示範在簡單案例中,當您可以存取樹狀中的所有目錄時應如何逐一查檔案目錄,檔案大小並不大,且存取時間不長。 此方法一開始由於正在建構檔案名稱陣列,會有一段時間的延遲。
// Use Directory.GetFiles to get the source sequence of file names.
public static void FileIterationOne(string path)
{
var sw = Stopwatch.StartNew();
int count = 0;
string[]? files = null;
try
{
files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
}
catch (UnauthorizedAccessException)
{
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 {path} was not found.");
}
var fileContents =
from FileName in files?.AsParallel()
let extension = Path.GetExtension(FileName)
where extension == ".txt" || extension == ".htm"
let Text = File.ReadAllText(FileName)
select new
{
Text,
FileName
};
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 uae)
{
Console.WriteLine(uae.Message);
return true;
}
return false;
});
}
Console.WriteLine($"FileIterationOne processed {count} files in {sw.ElapsedMilliseconds} milliseconds");
}
本範例示範在簡單案例中,當您可以存取樹狀中的所有目錄時應如何逐一查檔案目錄,檔案大小並不大,且存取時間不長。 此方法開始產生結果的速度會比上一個範例快。
public static void FileIterationTwo(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 FileName in fileNames.AsParallel()
let extension = Path.GetExtension(FileName)
where extension == ".txt" || extension == ".htm"
let Text = File.ReadAllText(FileName)
select new
{
Text,
FileName
};
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 uae)
{
Console.WriteLine(uae.Message);
return true;
}
return false;
});
}
Console.WriteLine($"FileIterationTwo processed {count} files in {sw.ElapsedMilliseconds} milliseconds");
}
使用 GetFiles 時,請確定您有足夠的權限可以存取樹狀中的所有目錄。 否則,將會擲回例外狀況,而且不會傳回任何結果。 在 PLINQ 查詢中使用 EnumerateDirectories 時,要以可讓您繼續逐一查看的正常方式處理 I/O 例外狀況會有困難。 如果您的程式碼必須處理 I/O 或未經授權的存取例外狀況,您應該考慮如何:使用平行類別逐一查看檔案目錄中描述的方法。
如果 I/O 延遲會造成問題,例如在網路上處理檔案 I/O,請考慮使用 TPL 和傳統 .NET 非同步程式設計,以及這篇部落格文章 (英文) 中所述的其中一項非同步 I/O 技術。
其他資源
文件
-
深入瞭解:PLINQ 資料範例
-
了解資料和工作平行處理原則中可能出現的錯誤,因為平行處理原則會增加循序程式碼中不會遇到的複雜度。
-
深入瞭解:操作說明:使用平行類別逐一查看檔案目錄