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.
Cuidado |
---|
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
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. |