Compartilhar via


Como: Iterar os diretórios de arquivos com PLINQ

Este exemplo mostra duas maneiras simples de paralelizar as operações em diretórios de arquivos. A primeira consulta usa o GetFiles método para preencher uma matriz de nomes de arquivo em um diretório e todos os subdiretórios. Este método não retorna até que toda a matriz é preenchida e, portanto, ele pode introduzir latência no início da operação. No entanto, depois que a matriz é preenchida, PLINQ pode processá-lo em paralelo muito rapidamente.

A segunda consulta usa estática EnumerateDirectories e EnumerateFiles métodos que começam retornando os resultados imediatamente. Essa abordagem pode ser mais rápida quando estiver iterando em árvores de diretório grande, embora o tempo de processamento em comparação comparado o primeiro exemplo pode depender de vários fatores.

Observação de cuidadoCuidado

Esses exemplos destinam-se para demonstrar o uso e podem não ser executado mais rápido do que o equivalente LINQ to Objects seqüencial de consulta de.Para obter mais informações sobre o aumento de velocidade, consulte Aumento de velocidade de compreensão no PLINQ.

Exemplo

O exemplo a seguir mostra como iterar nos diretórios de arquivos em cenários simples, quando você tem acesso a todos os diretórios na árvore, os tamanhos de arquivo não são muito grandes e os tempos de acesso não são significativos. Essa abordagem envolve um período de latência no início, enquanto a matriz de nomes de arquivo que está sendo construída.


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

O exemplo a seguir mostra como iterar nos diretórios de arquivos em cenários simples, quando você tem acesso a todos os diretórios na árvore, os tamanhos de arquivo não são muito grandes e os tempos de acesso não são significativos. Essa abordagem começa produzindo resultados mais rapidamente do que o exemplo anterior.


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

Ao usar o GetFiles, certifique-se de que você tenha permissões suficientes em todos os diretórios na árvore. Caso contrário, uma exceção será lançada e nenhum resultado será retornado. Ao usar o EnumerateDirectories em uma consulta PLINQ, é problemático para manipular exceções de i/O de forma normal que permite que você continue a iteração. Se o seu código deve lidar com exceções de acesso não autorizado ou e/S, então você deve considerar a abordagem descrita no Como: Iterar os diretórios de arquivos com a classe paralela.

Se a latência de i/O é um problema, por exemplo com e/S de arquivo em uma rede, considere usar uma das técnicas de e/S assíncronas descritas em A TPL e tradicionais.NET programação assíncrona e neste a postagem de blog.

Consulte também

Conceitos

Parallel LINQ PLINQ)

Histórico de alterações

Date

History

Motivo

Maio de 2010

Observação adicionada referentes ao uso vs. aumento de velocidade.

Comentários do cliente.