如何:使用 PLINQ 逐一查看檔案目錄How to: Iterate File Directories with PLINQ

此範例示範兩個簡單的方式來平行處理檔案目錄的作業。This example shows two simple ways to parallelize operations on file directories. 第一個查詢使用 GetFiles 方法來填入目錄和所有子目錄中的檔案名稱陣列。The first query uses the GetFiles method to populate an array of file names in a directory and all subdirectories. 這個方法在整個陣列填入之前不會傳回,因此在作業開始時可能會延遲。This method does not return until the entire array is populated, and therefore it can introduce latency at the beginning of the operation. 不過,在填入陣列之後,PLINQ 可以平行方式非常快速地處理它。However, after the array is populated, PLINQ can process it in parallel very quickly.

第二個查詢使用靜態 EnumerateDirectoriesEnumerateFiles 方法,會立即開始傳回結果。The second query uses the static EnumerateDirectories and EnumerateFiles methods which begin returning results immediately. 逐一查看大型的目錄樹狀時,這種方法的執行速度更快,然而相較於第一個範例,處理時間可能取決於許多因素。This approach can be faster when you are iterating over large directory trees, although the processing time compared to the first example can depend on many factors.

警告

這些範例是為了示範用法,執行速度可能比不上對應的循序 LINQ to Objects 查詢。These examples are intended to demonstrate usage, and might not run faster than the equivalent sequential LINQ to Objects query. 如需加速的詳細資訊,請參閱認識 PLINQ 中的加速For more information about speedup, see Understanding Speedup in PLINQ.

範例Example

下列範例示範在簡單案例中,當您可以存取樹狀中的所有目錄時應如何逐一查檔案目錄,檔案大小不是非常大,且存取時間不是很長。The following example shows how to iterate over file directories in simple scenarios when you have access to all directories in the tree, the file sizes are not very large, and the access times are not significant. 此方法一開始由於正在建構檔案名稱陣列,會有一段時間的延遲。This approach involves a period of latency at the beginning while the array of file names is being constructed.


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);
    }

範例Example

下列範例示範在簡單案例中,當您可以存取樹狀中的所有目錄時應如何逐一查檔案目錄,檔案大小不是非常大,且存取時間不是很長。The following example shows how to iterate over file directories in simple scenarios when you have access to all directories in the tree, the file sizes are not very large, and the access times are not significant. 此方法開始產生結果的速度會比上一個範例快。This approach begins producing results faster than the previous example.


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 時,請確定您有足夠的權限可以存取樹狀中的所有目錄。When using GetFiles, be sure that you have sufficient permissions on all directories in the tree. 否則將會擲回例外狀況,而且不會傳回任何結果。Otherwise an exception will be thrown and no results will be returned. 在 PLINQ 查詢中使用 EnumerateDirectories 時,要以可讓您繼續逐一查看的正常方式處理 I/O 例外狀況會有困難。When using the EnumerateDirectories in a PLINQ query, it is problematic to handle I/O exceptions in a graceful way that enables you to continue iterating. 如果您的程式碼必須處理 I/O 或未經授權的存取例外狀況,您應該考慮如何:使用平行類別逐一查看檔案目錄中描述的方法。If your code must handle I/O or unauthorized access exceptions, then you should consider the approach described in How to: Iterate File Directories with the Parallel Class.

如果 I/O 延遲會造成問題,例如在網路上處理檔案 I/O,請考慮使用 TPL 和傳統 .NET Framework 非同步程式設計和這篇部落格文章中所述的其中一項非同步 I/O 技術。If I/O latency is an issue, for example with file I/O over a network, consider using one of the asynchronous I/O techniques described in TPL and Traditional .NET Framework Asynchronous Programming and in this blog post.

請參閱See also