Gewusst wie: Iterieren von Dateiverzeichnissen mit PLINQ

In diesem Beispiel werden zwei einfache Möglichkeiten gezeigt, Vorgänge für Dateiverzeichnisse zu parallelisieren. Die erste Abfrage verwendet die GetFiles-Methode, um ein Array von Dateinamen in einem Verzeichnis und allen Unterverzeichnissen aufzufüllen. Diese Methode wird erst zurückgegeben, wenn das gesamte Array aufgefüllt ist. Daher kann zu Beginn des Vorgangs Wartezeit auftreten. Nachdem das Array aufgefüllt wurde, kann es jedoch sehr schnell von PLINQ parallel verarbeitet werden.

In der zweiten Abfrage werden die statische EnumerateDirectories-Methode und die statische EnumerateFiles-Methode verwendet, die sofort Ergebnisse zurückgeben. Dieser Ansatz kann schneller sein, wenn umfangreiche Verzeichnisstrukturen durchlaufen werden, obwohl im Gegensatz zum ersten Beispiel die Verarbeitungszeit von vielen Faktoren abhängen kann.

WarnhinweisVorsicht

Diese Beispiele sollen die Verwendung veranschaulichen, und sie werden möglicherweise nicht schneller als die entsprechende sequenzielle LINQ to Objects-Abfrage ausgeführt.Weitere Informationen über Geschwindigkeitssteigerungen finden Sie unter Grundlagen zur Beschleunigung in PLINQ.

Beispiel

Im folgenden Beispiel wird gezeigt, wie in einfachen Szenarien, in denen Sie Zugriff auf alle Verzeichnisse in der Struktur haben, die Dateien nicht sehr groß sind und die Zugriffszeit nicht erheblich ist, Dateiverzeichnisse durchlaufen werden. Diese Vorgehensweise beinhaltet Wartezeit zu Beginn, während das Array von Dateinamen erstellt wird.


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

Im folgenden Beispiel wird gezeigt, wie in einfachen Szenarien, in denen Sie Zugriff auf alle Verzeichnisse in der Struktur haben, die Dateien nicht sehr groß sind und die Zugriffszeit nicht erheblich ist, Dateiverzeichnisse durchlaufen werden. Bei dieser Vorgehensweise werden Ergebnisse schneller als beim vorherigen Beispiel erzeugt.


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

Wenn Sie GetFiles verwenden, stellen Sie sicher, dass Sie über ausreichende Berechtigungen für alle Verzeichnisse in der Struktur verfügen. Andernfalls wird eine Ausnahme ausgelöst, und es werden keine Ergebnisse zurückgegeben. Bei Verwendung von EnumerateDirectories in einer PLINQ-Abfrage ist es schwierig, E/A-Ausnahmen ordnungsgemäß zu behandeln, damit die Iteration fortgesetzt werden kann. Wenn der Code E/A-Ausnahmen oder Ausnahmen aufgrund von unberechtigtem Zugriff behandeln muss, sollten Sie die in Gewusst wie: Iterieren von Dateiverzeichnissen der Parallel-Klasse beschriebene Vorgehensweise verwenden.

Wenn E/A-Wartezeit ein Problem ist, z. B. bei Datei-E/A in einem Netzwerk, können Sie eines der Verfahren für asynchrone E/A verwenden, die in TPL und herkömmliche asynchrone .NET-Programmierung und in diesem Blogbeitrag beschrieben werden.

Siehe auch

Konzepte

Paralleles LINQ (PLINQ)

Änderungsprotokoll

Datum

Versionsgeschichte

Grund

Mai 2010

Hinweis bezüglich Verwendung und Geschwindigkeitssteigerung hinzugefügt.

Kundenfeedback.