방법: PLINQ를 사용하여 파일 디렉터리 열거
이 문서에서는 파일 디렉터리에서 작업을 병렬 처리하는 두 가지 방법을 보여 줍니다. 첫 번째 쿼리는 GetFiles 메서드를 사용하여 디렉터리 및 모든 하위 디렉터리에서 파일 이름 배열을 채웁니다. 이 메서드는 전체 배열이 채워질 때까지 반환되지 않으므로 작업 시작 시 대기 시간이 발생할 수 있습니다. 그러나 배열이 채워진 후 PLINQ는 메서드를 빠르게 병렬 처리할 수 있습니다.
두 번째 쿼리는 결과를 즉시 반환하기 시작하는 정적 EnumerateDirectories 및 EnumerateFiles 메서드를 사용합니다. 큰 디렉터리 트리를 반복 중일 경우 이 방법이 더 빠를 수 있지만, 처리 시간은 첫 번째 예제와 비교하여 많은 요소에 따라 다릅니다.
참고 항목
이 예제는 사용법을 보여 주기 위해 제공되며 동일한 순차적 LINQ to Objects 쿼리보다 빠르게 실행되지 않을 수 있습니다. 속도 향상에 대한 자세한 내용은 PLINQ의 속도 향상 이해를 참조하세요.
GetFiles 예제
다음 예제에서는 트리의 모든 디렉터리에 액세스할 수 있고, 파일 크기가 크지 않으며, 액세스 시간이 길지 않을 경우 파일 디렉터리를 반복하는 방법을 간단한 시나리오로 보여 줍니다. 이 방법에는 파일 이름 배열이 생성되는 동안 시작 시에 대기 시간이 포함됩니다.
// Use Directory.GetFiles to get the source sequence of file names.
public static void FileIterationOne(string path)
{
var sw = Stopwatch.StartNew();
int count = 0;
string[]? files = null;
try
{
files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
}
catch (UnauthorizedAccessException)
{
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 {path} was not found.");
}
var fileContents =
from FileName in files?.AsParallel()
let extension = Path.GetExtension(FileName)
where extension == ".txt" || extension == ".htm"
let Text = File.ReadAllText(FileName)
select new
{
Text,
FileName
};
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 uae)
{
Console.WriteLine(uae.Message);
return true;
}
return false;
});
}
Console.WriteLine($"FileIterationOne processed {count} files in {sw.ElapsedMilliseconds} milliseconds");
}
EnumerateFiles 예제
다음 예제에서는 트리의 모든 디렉터리에 액세스할 수 있고, 파일 크기가 크지 않으며, 액세스 시간이 길지 않을 경우 파일 디렉터리를 반복하는 방법을 간단한 시나리오로 보여 줍니다. 이 방법은 이전 예제보다 빠르게 결과를 생성하기 시작합니다.
public static void FileIterationTwo(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 FileName in fileNames.AsParallel()
let extension = Path.GetExtension(FileName)
where extension == ".txt" || extension == ".htm"
let Text = File.ReadAllText(FileName)
select new
{
Text,
FileName
};
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 uae)
{
Console.WriteLine(uae.Message);
return true;
}
return false;
});
}
Console.WriteLine($"FileIterationTwo processed {count} files in {sw.ElapsedMilliseconds} milliseconds");
}
GetFiles를 사용할 경우 트리의 모든 디렉터리에 충분한 권한이 있는지 확인하세요. 그렇지 않으면 예외가 throw되고 결과가 반환되지 않습니다. PLINQ 쿼리에서 EnumerateDirectories를 사용할 경우 반복을 계속할 수 있는 정상적인 방법으로 I/O 예외를 처리하는 것이 어렵습니다. 코드에서 I/O 또는 인증되지 않은 액세스 예외를 처리해야 하는 경우에는 방법: 병렬 클래스를 사용하여 파일 디렉터리 반복에 설명된 방법을 고려해야 합니다.
예를 들어 I/O 지연이 네트워크를 통한 파일 I/O와 관련된 문제인 경우 TPL 및 일반적인 .NET 비동기 프로그래밍 및 이 블로그 게시물에 설명된 비동기 I/O 기법 중 하나를 사용하는 것이 좋습니다.
참고 항목
.NET
피드백
https://aka.ms/ContentUserFeedback
출시 예정: 2024년 내내 콘텐츠에 대한 피드백 메커니즘으로 GitHub 문제를 단계적으로 폐지하고 이를 새로운 피드백 시스템으로 바꿀 예정입니다. 자세한 내용은 다음을 참조하세요.다음에 대한 사용자 의견 제출 및 보기