방법: PLINQ를 사용하여 파일 디렉터리 열거
업데이트: 2010년 5월
이 예제에서는 파일 디렉터리에 대해 작업을 병렬화하는 간단한 두 가지 방법을 보여 줍니다. 첫 번째 쿼리에서는 GetFiles 메서드를 사용하여 디렉터리 및 모든 하위 디렉터리에 있는 파일 이름 배열을 채웁니다. 이 메서드는 전체 배열이 채워질 때까지 반환하지 않으므로 작업의 시작 부분에서 지연이 발생할 수 있습니다. 그러나 배열이 채워진 후 PLINQ는 아주 빨리 작업을 병렬로 처리할 수 있습니다.
두 번째 쿼리에서는 바로 결과를 반환하기 시작하는 정적 EnumerateDirectories 및 EnumerateFiles 메서드를 사용합니다. 이 방법은 처리 시간을 첫 번째 예제와 비교해 볼 때 여러 요인에 따라 달라질 수 있지만 큰 디렉터리 트리에 대해 반복하는 경우 첫 번째 예제보다 더 빠를 수 있습니다.
주의 |
---|
이 예제들은 사용법을 보여 주기 위한 것이며, 이에 상응하는 순차 LINQ to Objects 쿼리보다 실행 속도가 느릴 수 있습니다.속도 향상에 대한 자세한 내용은 PLINQ의 속도 향상 이해를 참조하십시오. |
예제
다음 예제에서는 트리의 모든 디렉터리에 대한 액세스 권한이 있고 파일 크기가 아주 크지 않고 액세스 시간이 길지 않은 간단한 시나리오에서 파일 디렉터리에 대해 반복하는 방법을 보여 줍니다. 이 방법을 사용할 경우 파일 이름 배열이 생성되고 있는 동안 시작 부분에서 지연 시간이 발생합니다.
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);
}
다음 예제에서는 트리의 모든 디렉터리에 대한 액세스 권한이 있고 파일 크기가 아주 크지 않고 액세스 시간이 길지 않은 간단한 시나리오에서 파일 디렉터리에 대해 반복하는 방법을 보여 줍니다. 이 방법을 사용하면 이전 예제보다 빨리 결과기 생성되기 시작합니다.
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를 사용할 때 트리의 모든 디렉터리에 대한 충분한 권한이 있어야 합니다. 그렇지 않으면 예외가 throw되고 결과가 반환되지 않습니다. PLINQ 쿼리에서 EnumerateDirectories를 사용할 때 반복을 계속할 수 있도록 정상적인 방법으로 I/O 예외를 처리하는 것이 문제가 될 수 있습니다. 코드를 통해 I/O 또는 무단 액세스 예외를 처리해야 하는 경우 방법: 병렬 클래스를 사용하여 파일 디렉터리 열거에 설명된 방법을 고려해야 합니다.
예를 들어 I/O 지연이 네트워크를 통한 파일 I/O와 관련된 문제인 경우 TPL 및 일반적인 .NET 비동기 프로그래밍 및 이 블로그 게시물에 설명된 비동기 I/O 방법 중 하나를 사용해 보십시오.
참고 항목
개념
변경 기록
날짜 |
변경 내용 |
이유 |
---|---|---|
2010년 5월 |
사용법과 속도 향상에 대한 설명을 추가했습니다. |
고객 의견 |