Condividi tramite


Procedura: scorrere le directory dei file con PLINQ

In questo esempio vengono illustrati due semplici modi di parallelizzare le operazioni nelle directory di file. La prima query utilizza il metodo GetFiles per popolare una matrice di nomi file in una directory e in tutte le sottodirectory. Questo metodo non restituisce alcun risultato finché l'intera matrice non è popolata, quindi è possibile che venga introdotta una certa latenza all'inizio dell'operazione. Tuttavia, dopo il popolamento della matrice, PLINQ è in grado di elaborarla in parallelo molto rapidamente.

La seconda query utilizza i metodi statici EnumerateDirectories e EnumerateFiles che iniziano a restituire i risultati immediatamente. Questo approccio può essere più rapido quando si esegue l'iterazione in strutture ad albero di directory di grandi dimensioni, anche se il tempo di elaborazione rispetto al primo esempio può dipendere da molti fattori.

Nota di avvisoAttenzione

Lo scopo di questi esempi è dimostrare l'utilizzo e potrebbero non essere eseguiti più velocemente dell'equivalente query LINQ to Objects sequenziale.Per ulteriori informazioni sull'aumento di velocità, vedere Informazioni sull'aumento di velocità in PLINQ.

Esempio

Il seguente esempio mostra come eseguire l'iterazione in directory di file in semplici scenari quando si dispone dell'accesso a tutte le directory della struttura ad albero, le dimensioni dei file non sono molto elevate e i tempi di accesso non sono significativi. Questo approccio prevede un periodo di latenza iniziale durante la costruzione della matrice dei nomi file.


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

Il seguente esempio mostra come eseguire l'iterazione in directory di file in semplici scenari quando si dispone dell'accesso a tutte le directory della struttura ad albero, le dimensioni dei file non sono molto elevate e i tempi di accesso non sono significativi. Questo approccio inizia a fornire i risultati più velocemente dell'esempio precedente.


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

Quando si utilizza GetFiles, accertarsi di disporre di autorizzazioni sufficienti per tutte le directory della struttura ad albero. In caso contrario verrà generata un'eccezione e non verrà restituito alcun risultato. Quando si utilizza EnumerateDirectories in una query PLINQ, è problematico gestire le eccezioni I/O in un modo che consenta di continuare l'iterazione. Se tramite il codice è necessario gestire eccezioni di I/O o di accesso non autorizzato, considerare l'approccio descritto in Procedura: scorrere le directory dei file con la classe Parallel.

Se la latenza di I/O costituisce un problema, ad esempio con l'I/O dei file su una rete, considerare la possibilità di utilizzare una delle tecniche di I/O asincrono descritte in Task Parallel Library e programmazione asincrona .NET tradizionale e in questo post di blog.

Vedere anche

Concetti

Parallel LINQ (PLINQ)

Cronologia delle modifiche

Data

Cronologia

Motivo

Maggio 2010

Aggiunta nota sull'utilizzo rispetto all'aumento di velocità.

Commenti e suggerimenti dei clienti.