다음을 통해 공유


방법: PLINQ를 사용하여 파일 디렉터리 열거

업데이트: 2010년 5월

이 예제에서는 파일 디렉터리에 대해 작업을 병렬화하는 간단한 두 가지 방법을 보여 줍니다. 첫 번째 쿼리에서는 GetFiles 메서드를 사용하여 디렉터리 및 모든 하위 디렉터리에 있는 파일 이름 배열을 채웁니다. 이 메서드는 전체 배열이 채워질 때까지 반환하지 않으므로 작업의 시작 부분에서 지연이 발생할 수 있습니다. 그러나 배열이 채워진 후 PLINQ는 아주 빨리 작업을 병렬로 처리할 수 있습니다.

두 번째 쿼리에서는 바로 결과를 반환하기 시작하는 정적 EnumerateDirectoriesEnumerateFiles 메서드를 사용합니다. 이 방법은 처리 시간을 첫 번째 예제와 비교해 볼 때 여러 요인에 따라 달라질 수 있지만 큰 디렉터리 트리에 대해 반복하는 경우 첫 번째 예제보다 더 빠를 수 있습니다.

주의 정보주의

이 예제들은 사용법을 보여 주기 위한 것이며, 이에 상응하는 순차 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 방법 중 하나를 사용해 보십시오.

참고 항목

개념

PLINQ(병렬 LINQ)

변경 기록

날짜

변경 내용

이유

2010년 5월

사용법과 속도 향상에 대한 설명을 추가했습니다.

고객 의견